//+---------------------------------------------------------------------------
//
//  Microsoft Forms
//  Copyright (C) Microsoft Corporation, 1994-1995
//
//  File:       ipwnd.cxx
//
//  Contents:   forms kernel window proc
//
//------------------------------------------------------------------------

#include "headers.hxx"

#pragma MARK_DATA(__FILE__)
#pragma MARK_CODE(__FILE__)
#pragma MARK_CONST(__FILE__)

#ifndef X_COMMCTRL_H_
#define X_COMMCTRL_H_
#include "commctrl.h"
#endif


#ifndef X_FRAME_HXX_
#define X_FRAME_HXX_
#include "frame.hxx"
#endif

#ifndef X_DOCGLBS_HXX_
#define X_DOCGLBS_HXX_
#include "docglbs.hxx"
#endif

#ifndef X_FORMKRNL_HXX_
#define X_FORMKRNL_HXX_
#include "formkrnl.hxx"
#endif

#ifndef X_OLEACC_H_
#define X_OLEACC_H_
#include "oleacc.h"
#endif

#ifndef UNIX
#ifndef X_WINABLE_H_
#define X_WINABLE_H_
#include "winable.h"
#endif
#endif

#ifndef X_INTL_HXX_
#define X_INTL_HXX_
#include "intl.hxx"
#endif

#ifndef X_WINDOW_HXX_
#define X_WINDOW_HXX_
#include "window.hxx"
#endif

#ifndef X_CSITE_HXX_
#define X_CSITE_HXX_
#include "csite.hxx"
#endif

#ifndef X_OLESITE_HXX_
#define X_OLESITE_HXX_
#include "olesite.hxx"
#endif

#ifndef X_TXTSITE_HXX_
#define X_TXTSITE_HXX_
#include "txtsite.hxx"
#endif

#ifndef X_HYPLNK_HXX_
#define X_HYPLNK_HXX_
#include "hyplnk.hxx"
#endif

#ifndef X_EANCHOR_HXX_
#define X_EANCHOR_HXX_
#include "eanchor.hxx"
#endif

#ifndef X_EAREA_HXX_
#define X_EAREA_HXX_
#include "earea.hxx"
#endif

#ifndef X_IMGELEM_HXX_
#define X_IMGELEM_HXX_
#include "imgelem.hxx"
#endif

#ifndef X_SHELL_H_
#define X_SHELL_H_
#include "shell.h"
#endif

#ifndef X_TREEPOS_HXX_
#define X_TREEPOS_HXX_
#include "treepos.hxx"
#endif

#ifndef X_EVNTPRM_HXX_
#define X_EVNTPRM_HXX_
#include "evntprm.hxx"
#endif

#ifndef X_MSHTMHST_H_
#define X_MSHTMHST_H_
#include <mshtmhst.h>
#endif

#ifndef X_ROOTELEMENT_HXX_
#define X_ROOTELEMENT_HXX_
#include "rootelem.hxx"
#endif

#ifndef X_FRAMESET_HXX_
#define X_FRAMESET_HXX_
#include "frameset.hxx"
#endif

#ifndef X_WCHDEFS_H_
#define X_WCHDEFS_H_
#include "wchdefs.h"
#endif

#ifndef X_INPUTTXT_HXX_
#define X_INPUTTXT_HXX_
#include "inputtxt.hxx"
#endif

#if !defined(NO_IME)
#ifndef WM_IME_REQUEST
#define WM_IME_REQUEST 0x0288
#endif
#ifndef IMR_RECONVERTSTRING
#define IMR_RECONVERTSTRING 0x0004
#endif
#endif

#ifdef UNIX

#include <mainwin.h>
extern "C" HANDLE MwGetPrimarySelectionData();
#include "quxcopy.hxx"

#endif //UNIX

#ifndef X_ACCWIND_HXX_
#define X_ACCWIND_HXX_
#include "accwind.hxx"
#endif

#ifndef X_ACCUTIL_HXX_
#define X_ACCUTIL_HXX_
#include "accutil.hxx"
#endif

EXTERN_C const GUID CGID_DocHostCommandHandler;

#ifndef X_OLEACC_H
#define X_OLEACC_H
#include <oleacc.h>
#endif

#ifndef WIN16
DYNLIB g_dynlibOLEACC = { NULL, NULL, "OLEACC.DLL" };
#endif // !WIN16

MtDefine(MsoCmdText, Locals, "MSOCMDTEXT structure (temp)")

// Holds the user defined windows message
UINT CDoc::_g_msgHtmlGetobject = 0;
#if !defined(NO_IME)
UINT CDoc::_g_msgImeReconvert = 0;
#endif // !NO_IME

ExternTag(tagRects);
DeclareTag(tagCapture, "DocCapture", "trace SetMouseCapture")
DeclareTag(tagPaintRedraw, "Doc", "trace WM_PAINT, WM_SETREDRAW");

extern HRESULT EnsureAccWindow( CWindow * pWindow );

extern void RestartImgAnimTimerProc();
extern void KillImgAnimTimerProc();

#define WM_CHECKMINIMIZED (WM_USER + 145)

//+---------------------------------------------------------------
//
// Local Helper: ShowTooltipHelper
//
//----------------------------------------------------------------
void
ShowTooltipHelper(CDoc * pDoc, CTreeNode * pNodeContext, CMessage * pMsg)
{
    CMarkup * pMarkup = pNodeContext ? pNodeContext->GetMarkup() : NULL;

    if (!(pMarkup && pMarkup->_fDesignMode))
    {
        BOOL    fDismissed;

        // Ignore spurious WM_ERASEBACKGROUNDs generated by tooltips
        CServer::CLock Lock(pDoc, SERVERLOCK_IGNOREERASEBKGND);

        //
        //  Give tooltips a chance to dismiss
        //
        fDismissed = FormsTooltipMessage(
                pMsg->message,
                pMsg->wParam,
                pMsg->lParam);

        // If we're not captured and the tooltip is being dismissed....
        //
        if (fDismissed == FALSE)
        {
            //  If hitted element has tooltip, put up its tooltip, otherwise,
            //  Walk through the element hierarchy.  If any element
            //  has tooltip text, put up the tooltip.
            //
            CTreeNode * pNode = pNodeContext;
            for (; pNode && pNode->Tag() != ETAG_ROOT ;
                       pNode = pNode->Parent())
            {
                if (pNode->Element()->ShowTooltip(pMsg, pMsg->pt) != S_FALSE)
                {
                    break;
                }
            }
        }
    }
}


//+====================================================================================
//
// Method: IsTridentHWND
//
// Synopsis: Helper to check to see if a given HWND belongs to a trident window
//
//------------------------------------------------------------------------------------


BOOL
IsTridentHwnd( HWND hwnd )
{
    TCHAR strClassName[100] ;

    ::GetClassName( hwnd, strClassName, 100 );

    if ( StrCmpIW( strClassName, _T("Internet Explorer_Server") ) == 0 )
    {
        return TRUE;
    }
    else
        return FALSE;
}

//+====================================================================================
//
// Method: OnChildBeginSelection
//
// Synopsis: Called by enumeration of all child windows 
//           If you are a TridentHwnd - post a WM_BEGINSELECTION message to yourself.
//           Otherwise do nothing
//
//------------------------------------------------------------------------------------

BOOL
CALLBACK
OnChildBeginSelection( HWND hwnd, LPARAM lParam )
{
    if ( IsTridentHwnd( hwnd ) )
    {
        ::SendMessage( hwnd,
                       WM_BEGINSELECTION,
                       lParam,  // Cascade the Selection Type.
                       0 );
    }
    return TRUE;
}

BOOL
CDoc::IsPopupChildHwnd( HWND hwnd )
{
    CDoc *pDocChild = _pDocPopup;

    while (pDocChild)
    {
        if (    pDocChild->_pInPlace->_hwnd == hwnd
            ||   ::IsChild(pDocChild->_pInPlace->_hwnd, hwnd))
            return TRUE;

        pDocChild = pDocChild->_pDocPopup;
    }

    return FALSE;
}


#ifdef UNIX
//+---------------------------------------------------------------------------
//
//  Member:     MwTransalteUnixKeyBinding
//
//  Synopsis:   This routine acts as a late stage TranslateAccelerator call with
//              the caveat that if translated only the wParam and Modifiers 
//              arguments will be changed.
//
//              This means that for the KEYDOWN/KEYUP messages the key code and 
//              modifier state could be translated to something else.  This 
//              implies GetKeyState for the modifiers will not be re-queried 
//              after a call to this function as it might not be the same as the
//              now translated modifier state.
//
//  Returns:    void
//
//----------------------------------------------------------------------------
extern "C" BOOL MwTranslateUnixKeyBinding( HWND hwnd, DWORD message, 
                                           WPARAM *pwParam, DWORD *pModifiers );
#endif

//+---------------------------------------------------------------
//
//  Member:     CDoc::OnWindowMessage
//
//  Synopsis:   Handle window messages dispatched from the wndproc.
//
//  Returns:    S_FALSE if message should be passed on to the
//              default window procedure.
//
//---------------------------------------------------------------

HRESULT
CDoc::OnWindowMessage(
        UINT msg,
        WPARAM wParam,
        LPARAM lParam,
        LRESULT *plResult)
{
    HRESULT     hr;
    HWND        hwnd = _pInPlace->_hwnd;
    HWND        hwndControl = NULL;
    CLock       Lock(this);
#ifdef UNIX
    BOOL        bCutToEOL = FALSE;
    BOOL        bCutFullLine = FALSE;
#endif
    BOOL        fWeJustHidSelection = FALSE;
    HWND        hwndParent = NULL;
    HWND        hwndLoseFocus = NULL;
    HWND        hwndTemp = NULL;
    BOOL        fTridentHwnd = FALSE;
    BOOL        fLosingFocusToTridentChild;
    CMarkup*    pCurMarkup;

    Assert(_pElemCurrent);

    hr = S_OK;
    *plResult = 0;
    if (    _pDocPopup 
        &&  msg >= WM_KEYFIRST 
        &&  msg <= WM_KEYLAST
        &&  _pDocPopup->_pInPlace)
    {
        HRESULT hr2 = S_OK;
        hr2 = THR(_pDocPopup->OnWindowMessage(msg, wParam, lParam, plResult));
        if (S_OK == hr2)
            goto Cleanup;
    }

#if DBG == 1
    if (msg == WM_PAINT || msg == WM_SETREDRAW)
    {
        TraceTag((tagPaintRedraw, "%x +%d %x %x",
                        this, msg, wParam, lParam));
    }
#endif

    switch (msg)
    {
    case WM_APPCOMMAND:
    case WM_INPUTLANGCHANGE:
        {
            CMessage Message(_pInPlace->_hwnd, msg, wParam, lParam);
            PumpMessage(&Message, _pElemCurrent->GetFirstBranch());
        }
        break;

#ifndef NO_MENU
     case WM_MENUSELECT:
     case WM_INITMENUPOPUP:
        {
            CMessage Message(_pInPlace->_hwnd, msg, wParam, lParam);

            if (_pMenuObject)
            {
                PumpMessage(&Message, _pMenuObject->GetFirstBranch());
            }
            else
            {
                PumpMessage(&Message, _pElemCurrent->GetFirstBranch());
            }
        }
        break;
#endif // NO_MENU

     case WM_TIMER:

        if (wParam == TIMER_DEFERUPDATEUI)
        {
            OnUpdateUI();
        }
        break;
            

    //
    // Somewhere inside us a Selection is begin made. We clear any selection we have
    // and post the message on to all our child windows.
    //
    case WM_BEGINSELECTION:
    {
        IGNORE_HR( NotifySelection( EDITOR_NOTIFY_LOSE_FOCUS_FRAME, NULL, wParam ));

        ::EnumChildWindows( _pInPlace->_hwnd,
                            (WNDENUMPROC) OnChildBeginSelection,
                            wParam );
    }
    break;
    case WM_KILLFOCUS:
    {
        _pInPlace->_fFocus = FALSE;
        // Release any mouse capture when we loose focus
        SetMouseCapture (NULL, NULL);

        _fGotKeyUp = TRUE;

        //
        //  Tell the editor to lose focus
        //
        //
        // marka - check to see if 
        // a) we're losing focus to another frameset (we kill our selection )
        // b) we're losing focus to another window ( we hide our selection )
        // c) just losing focus - we call Lose Focus ( to hide the caret ).
        //


        //
        // See if we're losing focus to another Window in the same window as us.
        //

        hwndTemp  = _pInPlace->_hwnd;
        while(hwndTemp)
        {
            hwndParent = hwndTemp;
            hwndTemp = GetParent(hwndTemp);
        }

        fLosingFocusToTridentChild = FALSE;
        hwndTemp = (HWND) wParam;
        while(hwndTemp)
        {      
            hwndLoseFocus = hwndTemp;

            if (hwndLoseFocus == _pInPlace->_hwnd)
            {
                fLosingFocusToTridentChild = TRUE;

                // shortcut the serach, because we already
                // know the top-level parent in this case
                hwndLoseFocus = hwndParent;
                break;
            }

            hwndTemp = GetParent(hwndTemp);
        }

        fTridentHwnd = wParam && IsTridentHwnd( (HWND) wParam ) ;
        
        if ( hwndLoseFocus != hwndParent && HasTextSelection() ) 
        {
            CMarkup *pMarkup = GetCurrentMarkup();

            if( pMarkup )
            {
                pMarkup->HideSelection();
                SET_EDIT_BIT( pMarkup, _fSelectionHidden , TRUE)
                fWeJustHidSelection = TRUE;
            }
        }
        else  if ( hwndLoseFocus == hwndParent && fTridentHwnd )
        {
#if 0        
           hr = THR( NotifySelection( EDITOR_NOTIFY_LOSE_FOCUS_FRAME, NULL ));
#endif            
        }
        else
        {
            hr = THR( NotifySelection( EDITOR_NOTIFY_LOSE_FOCUS, NULL ));
        }
        
        // If losing focus to a window that is not a Trident child, but is a
        // child of Trident's top-level parent (for example, the address box
        // of IE), clear the first-time-tab flag (37950)
        if (!fLosingFocusToTridentChild && hwndParent == hwndLoseFocus)
        {
            _fFirstTimeTab = FALSE;
        }

        if (    _pDocPopup
            &&  (   GetWindowThreadProcessId((HWND) wParam, NULL)
                        != GetWindowThreadProcessId(_pInPlace->_hwnd, NULL)
                ||  !IsPopupChildHwnd( (HWND) wParam ))
            )
        {
            IGNORE_HR(_pDocPopup->DoVerb(OLEIVERB_HIDE,
                                        NULL,
                                        _pDocPopup->_pClientSite,
                                        0,
                                        NULL,
                                        NULL));
        }

        // check if we go iconic
        ::PostMessage(hwnd, WM_CHECKMINIMIZED, NULL, NULL);

    }           // Fall through

    case WM_SETFOCUS:
    {
        // check if we go maximized / restored
        if ( _fIconic && (msg == WM_SETFOCUS))
        {
            while (hwnd)
            {
                hwndParent = hwnd;
                hwnd = GetParent(hwnd);
            }
        
            if ( !IsIconic( hwndParent ) )
            {
                _fIconic = FALSE;
                InternetSetOption(NULL, INTERNET_OPTION_RESTORE_WORKER_THREAD_DEFAULTS , NULL, NULL); 
                RestartImgAnimTimerProc();
            }
        }

        CMessage  Message(_pInPlace->_hwnd, msg, wParam, lParam);

        CElement *pElemFireTarget;

        pElemFireTarget = _pElemCurrent->GetFocusBlurFireTarget(_lSubCurrent);
        Assert(pElemFireTarget);

        // Whenever we get focus, we set the flag to false to indicate
        // that we have not received a key down, so donot fire the keyups
        if (WM_SETFOCUS == msg)
        {
            _fGotKeyDown = FALSE;
            // Do not fire window onfocus if we are here as a result of onblur\onfocus
            // bringing up a modal dialog. However, we do want to fire it if window onblur
            // brought up a modal dialog that was dismissed.
            if (!TestLock(FORMLOCK_CURRENT) ||
                (   !pElemFireTarget->TestLock(CElement::ELEMENTLOCK_FOCUS)
                 && !pElemFireTarget->TestLock(CElement::ELEMENTLOCK_BLUR)
                 && _fModalDialogInOnblur
                )
               )
            {
                GetCurrentWindow()->Post_onfocus();
            }
        }

        if (    _pInPlace
            &&  !_pInPlace->_fDeactivating
            &&  !TestLock(FORMLOCK_CURRENT))
        {
            // Do not fire site onfocus\onblur if we come here either
            // as a result of calling blur() or focus(), because it would
            // have been already fired in BecomeCurrent()
            if (!_fInhibitFocusFiring)
            {
                // SELECT, being a windowed control should receive WM_KILLFOCUS directly
                if (WM_SETFOCUS == msg || (WM_KILLFOCUS == msg && _pElemCurrent->Tag() != ETAG_SELECT))
                {
                    // if the doc is not locked, the elem can't be locked either
                    Assert(!pElemFireTarget->TestLock(CElement::ELEMENTLOCK_FOCUS));
                    Assert(!pElemFireTarget->TestLock(CElement::ELEMENTLOCK_BLUR));

                    // fire focus/blur events
                    _pElemCurrent->Fire_ActivationHelper(
                                                        _lSubCurrent,
                                                        NULL,
                                                        0,
                                                        FALSE,
                                                        (WM_KILLFOCUS == msg),
                                                        TRUE, 
                                                        NULL,
                                                        FALSE);
                }

                if (WM_SETFOCUS == msg)
                {
                    // Fire window onblur if onfocus previously fired and the
                    // current site is not the body
                    if (_pElemCurrent->IsInMarkup() && _pElemCurrent != _pElemCurrent->GetMarkup()->GetElementClient())
                    {
                        GetCurrentWindow()->Post_onblur();
                    }
                }
            }
        }
        // we get here if a modal dialog from current element's onblur is dismissed.
        // In this case we wan't to fire its onfocus again.
        else if (pElemFireTarget->TestLock(CElement::ELEMENTLOCK_BLUR) &&
                 WM_SETFOCUS == msg && _fModalDialogInOnblur)
        {
            GWPostMethodCall(pElemFireTarget, ONCALL_METHOD(CElement, Fire_onfocus, fire_onfocus), 0, TRUE, "CElement::Fire_onfocus");
        }

        if (_view.IsActive())
        {
            _view.InvalidateFocus();

            // Display the caret
            if (_pCaret &&  WM_SETFOCUS == msg)
            {
                _pCaret->UpdateCaret();
            }
        }

        pCurMarkup = GetCurrentMarkup();        
        if ( !fWeJustHidSelection && pCurMarkup ) 
        {     
            if ( CHECK_EDIT_BIT( pCurMarkup, _fSelectionHidden ))
            {
                pCurMarkup->ShowSelection();                 
                SET_EDIT_BIT( pCurMarkup, _fSelectionHidden , FALSE)            
            }
            else if ( HasTextSelection() )
            {
                // Always inval the selection here. Fixes problems with inval from Alerts fired OnSelectStart
                pCurMarkup->InvalidateSelection( ); 
            }                
        }

        IGNORE_HR(InvalidateDefaultSite());

        IGNORE_HR(PumpMessage(&Message, _pElemCurrent->GetFirstBranch()));

        // Forward the WM_KILLFOCUS, WM_SETFOCUS messages to CServer which
        // will notify the control site of the focus change.  Forward the
        // messages when no site has focus or when the site with focus is
        // a dataframe (non ole site).
        if (!_pElemCurrent->TestClassFlag(CElement::ELEMENTDESC_OLESITE))
            hr = THR(CServer::OnWindowMessage(msg, wParam, lParam, plResult));

        // Do not fire window onblur if we are here as a result of onblur\onfocus
        // bringing up a modal dialog.
        if (WM_KILLFOCUS == msg && !TestLock(FORMLOCK_CURRENT))
        {
            GetCurrentWindow()->Post_onblur(TRUE);
        }

        break;
    }

    case WM_CAPTURECHANGED:
#if DBG==1
        TLS(fHandleCaptureChanged) = TRUE;
#endif
        SetMouseCapture(NULL, NULL);

#if DBG==1
        TLS(fHandleCaptureChanged) = FALSE;
#endif
        break;

    //
    // Messages sent to site under mouse.
    //

    case WM_SETCURSOR:
        if (LOWORD(lParam) == HTCLIENT)
        {
            POINT pt;

            GetCursorPos(&pt);
            ScreenToClient(hwnd, &pt);

            hr = THR(OnMouseMessage(
                    msg,
                    wParam,
                    lParam,
                    plResult,
                    pt.x, pt.y));

            //we couldn't find who is under us (BODY having "display:none" is example)
            //use default processing (it will eventually set arrow cursor)
            if(hr != S_OK)
                hr = CServer::OnWindowMessage(msg, wParam, lParam, plResult);
        }
        else
        {
            hr = CServer::OnWindowMessage(msg, wParam, lParam, plResult);
        }

        break;

    case WM_CONTEXTMENU:
        {
            POINT pt;
            pt.x = MAKEPOINTS(lParam).x;
            pt.y = MAKEPOINTS(lParam).y;
            if ( (pt.x  == -1 && pt.y == -1) && _pElemCurrent )
            {
                // pt.x & pt.y are supposed to be in screen coordinates;
                // the only case when they are -1,-1 is if WM_CONTEXTMENU
                // originated from Shift-F10 or Windows keyboard
                // key 'Menu'; in this case we send the message to the
                // current site.

                CMessage  Message(_pInPlace->_hwnd, msg, wParam, lParam);
                hr = THR(PumpMessage(&Message, _pElemCurrent->GetFirstBranch()));
            }
            else
            {               
                ScreenToClient(_pInPlace->_hwnd, &pt);

                // CONSIDER: should this set the focus before
                // sending the OnMouseMessage.  Does anything actually
                // use this code path? (jbeda)

                hr = THR(OnMouseMessage(
                        msg,
                        wParam,
                        lParam,
                        plResult,
                        pt.x, pt.y));
            }
        }
        break;

#ifndef WIN16
    case WM_MOUSEWHEEL:
HandleMouseWheel:
        if (_pDocPopup)
        {
            SendMessage(_pDocPopup->_pInPlace->_hwnd, msg, wParam, lParam);
        }
        else
        {
            POINT ptCursor;
            RECT  rcCurrent;

            // Check where the wheel rotates.
            // If the wheel is rotated inside the current CDoc window, let
            // us handle it. Otherwise, let DefWindowProc and handle it and
            // bubble to the parent window.
            ptCursor.x = MAKEPOINTS(lParam).x;
            ptCursor.y = MAKEPOINTS(lParam).y;
            ::GetWindowRect(InPlace()->_hwnd, &rcCurrent);

            if (PtInRect(&rcCurrent, ptCursor))
            {
                ScreenToClient(hwnd, &ptCursor);
                Assert(msg == WM_MOUSEWHEEL || msg == g_msgMouseWheel);
                hr = THR(OnMouseMessage(
                        WM_MOUSEWHEEL,
                        wParam,
                        lParam,
                        plResult,
                        ptCursor.x,
                        ptCursor.y));
            }
            else
            {
                hr = THR(CServer::OnWindowMessage(msg, wParam, lParam, plResult));
            }
        }
        break;
#endif // ndef WIN16

    //
    // Messages sent to site under mouse
    //

    case WM_LBUTTONDOWN:
    case WM_RBUTTONDOWN:
    case WM_MBUTTONDOWN:
    case WM_LBUTTONDBLCLK:
    case WM_RBUTTONDBLCLK:
    case WM_MBUTTONDBLCLK:
        switch (msg)
        {
        case WM_LBUTTONDOWN:
            _fCanFireDblClick = TRUE;
            // fall through
        
        case WM_LBUTTONDBLCLK:
            _fGotLButtonDown = TRUE;
            break;
        case WM_MBUTTONDOWN:
        case WM_MBUTTONDBLCLK:
            _fGotMButtonDown = TRUE;
            break;
        case WM_RBUTTONDOWN:
        case WM_RBUTTONDBLCLK:
            _fGotRButtonDown = TRUE;
            break;
        }
        _fGotKeyUp = FALSE;
        hr = THR(OnMouseMessage(
                msg,
                wParam,
                lParam,
                plResult,
                MAKEPOINTS(lParam).x, MAKEPOINTS(lParam).y));
        break;

    case WM_LBUTTONUP:
        if (!_fGotLButtonDown)
        {
            hr = S_OK;
            goto Cleanup;
        }
        _fGotLButtonDown = FALSE;
        hr = THR(OnMouseMessage(
                msg,
                wParam,
                lParam,
                plResult,
                MAKEPOINTS(lParam).x, MAKEPOINTS(lParam).y));
        break;

    case WM_MBUTTONUP:
        if (!_fGotMButtonDown)
        {
            hr = S_OK;
            goto Cleanup;
        }
        _fGotMButtonDown = FALSE;
        hr = THR(OnMouseMessage(
                msg,
                wParam,
                lParam,
                plResult,
                MAKEPOINTS(lParam).x, MAKEPOINTS(lParam).y));
        break;

    case WM_RBUTTONUP:
        if (!_fGotRButtonDown)
        {
            hr = S_OK;
            goto Cleanup;
        }
        _fGotRButtonDown = FALSE;
        hr = THR(OnMouseMessage(
                msg,
                wParam,
                lParam,
                plResult,
                MAKEPOINTS(lParam).x, MAKEPOINTS(lParam).y));
        break;

    case WM_MOUSEMOVE:
        hr = THR(OnMouseMessage(
                msg,
                wParam,
                lParam,
                plResult,
                MAKEPOINTS(lParam).x, MAKEPOINTS(lParam).y));
        break;

    case WM_MOUSELEAVE:
        // Default messages get forwarded to the base class's
        // window procedure.
        hr = THR(CServer::OnWindowMessage(msg, wParam, lParam, plResult));
        break;

    case WM_NCLBUTTONDOWN:
    {
        RECT    rcCurrent;

        if (OnNCLButtonDown(wParam, MAKEPOINTS(lParam), &rcCurrent))
        {
            SIZE size;

            TraceTag((tagRects,
                    "%08x OnNCLButtonDown > OnPosRectChange %d %d %d %d",
                    this, rcCurrent));

            HimetricFromDevice(size,
                    rcCurrent.right - rcCurrent.left,
                    rcCurrent.bottom - rcCurrent.top);
            SetExtent(DVASPECT_CONTENT, &size);

            IGNORE_HR(_pInPlace->_pInPlaceSite->OnPosRectChange(ENSUREOLERECT(&rcCurrent)));
        }
        break;
    }

    case WM_HELP:
        hr = THR(OnHelp((HELPINFO *)lParam));
        break;


    //
    // Keyboard messages: (Sent to _pElemCurrent)
    //

#ifdef UNIX
    //
    // Two new windows messages under Unix.  Kind of ironic huh?
    // These are generated from the TranslateUnixKeyBinding call
    // in the case where Ctrl-K (cut to the end of the line) or
    // Ctrl-U (cut full line) is pressed.
    //
    case WM_CUTTOEOL:
        bCutToEOL = TRUE;
        // fall thru
#endif
    case WM_KEYDOWN:
        /*
        if (_fPopupDoc && wParam == VK_ESCAPE)
        {
            // close Popup window if esc key pressed
            Assert(_pClientSite);

            hr = THR(DoVerb(    OLEIVERB_HIDE,
                                NULL,
                                _pClientSite,
                                0,
                                NULL,
                                NULL));
            if (hr)
                goto Cleanup;
            break;
        }
        */
        _fGotKeyDown = TRUE;
        // fall thru
    case WM_KEYUP:
        if (!_fGotKeyDown)
        {
            // NOTE (sujalp): Do not reset the _fGotKeyDown to
            // FALSE here. It is set to FALSE only when we get
            // the focus and that too to eat up the spurious key
            // up which we might get after we GOT_FOCUS.
            hr = S_OK;
            goto Cleanup;
        }
        // fall thru
    case WM_CHAR:
    case WM_DEADCHAR:
    case WM_SYSKEYDOWN:
    case WM_SYSKEYUP:
    case WM_SYSCHAR:
    case WM_SYSDEADCHAR:

#ifndef NO_IME
    case WM_IME_SETCONTEXT:
    case WM_IME_NOTIFY:
    case WM_IME_CONTROL:
    case WM_IME_COMPOSITIONFULL:
    case WM_IME_SELECT:
    case WM_IME_CHAR:
    case WM_IME_KEYDOWN:
    case WM_IME_KEYUP:
    case WM_IME_STARTCOMPOSITION:
    case WM_IME_ENDCOMPOSITION:
    case WM_IME_COMPOSITION:
    case WM_IME_REQUEST:
ReconvertIME:
#endif // !NO_IME
    {
        CMessage Message(_pInPlace->_hwnd, msg, wParam, lParam);

#if !defined(NO_IME)
        // Simulate IME Reconversion message
        if (msg == _g_msgImeReconvert)
        {
            Message.message = WM_IME_REQUEST;
            Message.wParam = IMR_RECONVERTSTRING;
        }
#endif // !(NO_IME)
        
#ifdef UNIX
        //
        // See MwTranslateUnixKeyBinding above for what it does.  
        // Basically we allow keys to be translated from emacs bindings
        // keys to windows keys.
        //


        if ((msg == WM_KEYDOWN) ||
            (msg == WM_KEYUP)) {
            BOOL bTranslated;

            bTranslated = MwTranslateUnixKeyBinding( Message.hwnd,
                                                     Message.message, 
                                                     &Message.wParam, 
                                                     &Message.dwKeyState );

            if ( bTranslated &&
                 msg == WM_KEYDOWN &&
                 Message.wParam == VK_DELETE ) {

                //
                // VK_DELETE is normally translated via resource
                // accelerators into an IDM_DELETE.  That code path
                // at this point has already passed though... so we
                // do it manually.
                //
                MSOCMD msocmd;

                msocmd.cmdID = IDM_DELETE;
                msocmd.cmdf = 0;

                QueryStatus((GUID *) &CGID_MSHTML,
                            1,
                            &msocmd,
                            NULL);
            
                if ( msocmd.cmdf != MSOCMDSTATE_DISABLED ) {
                    
                    Exec((GUID *)&CGID_MSHTML, IDM_DELETE, 0, NULL, NULL);
                }

                hr = S_OK;
                break;
            }
        }

        if ( bCutToEOL ) {
            //
            // Bit of a hack here.  When we're instructed to cut the 
            // current line or to the end of the current line we 
            // spoof the appropriate keystrokes into PumpMessage.
            //

            if ( Message.wParam == TRUE ) {
                bCutFullLine = TRUE;

                Message.message = WM_KEYDOWN;
                Message.wParam = VK_HOME;
                Message.lParam = 0L;
                Message.dwKeyState = 0;
            } else {
 CutToEOL:
                Message.message = WM_KEYDOWN;
                Message.wParam  = VK_END;
                Message.lParam  = 0L;
                Message.dwKeyState = FSHIFT;

                //
                // However, in order to not mess up the currently highlighted
                // item we first hide the selection.  This assures we don't
                // overwrite the current selection copy buffer.
                //
                // (Under Unix the currently selected item of which there are
                //  at most one can be pasted by pressing the middle mouse button)
                //

#if 0
                pTxtSite = _pSiteCurrent->GetTxtSite();
                if ( pTxtSite ) {
                    pTxtSite->GetSel(&pSel,FALSE);
                    if (pSel) {
                        pSel->ShowSelection(FALSE);
                    }
                }
#else

#endif
            }
        }
#endif // UNIX

        hr = S_FALSE;

        //
        // If the captured site didn't handle it, pass message to current
        // site.
        //

        if (S_FALSE == hr)
        {
            hr = THR(PumpMessage(&Message, _pElemCurrent->GetFirstBranch()));
            *plResult = Message.lresult;
        }
        DeferUpdateUI();

#ifdef UNIX_NOTYET
        //
        // Tail portion of cut line hack
        //

        if ( bCutFullLine ) {
            bCutFullLine = FALSE;
            goto CutToEOL;
        }

        if ( bCutToEOL ) {
            LRESULT lResult;
            OnWindowMessage(WM_CUT, 0, 0L, &lResult);
            if ( pSel ) {
                pSel->ShowSelection(TRUE);
            }
        }
#endif
        break;
    }

    //
    //  Messages that are either handled or reflected.
    //

    case WM_COMMAND:
        if (!_pMenuObject &&
                lParam &&
                GetParent(GET_WM_COMMAND_HWND(wParam, lParam)) == _pInPlace->_hwnd)
        {
            // Command is bubbling up from a control. Reflect it back.

            hwndControl = GET_WM_COMMAND_HWND(wParam, lParam);
            goto ReflectMessage;
        }
        else
        {
            // It's our command.
            OnCommand(GET_WM_COMMAND_ID(wParam, lParam), GET_WM_COMMAND_HWND(wParam, lParam), GET_WM_COMMAND_CMD(wParam, lParam));
        }
        break;

    case WM_DEFERZORDER:
        {
            FixZOrder();
        }
        break;

    case WM_ACTIVEMOVIE:
        {
            CNotification   nf;

            nf.ActiveMovie(PrimaryRoot(), (void *)lParam);
            BroadcastNotify(&nf);
        }
        break;

    //
    //  OLE Control v1.0 reflected messages
    //

    case WM_DRAWITEM:
        hwndControl = ((DRAWITEMSTRUCT *) lParam)->hwndItem;
        goto ReflectMessage;

    case WM_MEASUREITEM:
        //  TODO how did the control ID ever get set?
        hwndControl = GetDlgItem(hwnd, (UINT) wParam);
        goto ReflectMessage;

    case WM_DELETEITEM:
        hwndControl = ((DELETEITEMSTRUCT *) lParam)->hwndItem;
        goto ReflectMessage;

    case WM_COMPAREITEM:
        hwndControl = ((COMPAREITEMSTRUCT *) lParam)->hwndItem;
        goto ReflectMessage;

    case WM_NOTIFY:
#if DBG == 1 && !defined(WINCE) && defined(DBG_TOOLTIPS)
        if (DbgExIsFullDebug())
        {
            if ((TTN_NEEDTEXTA == ((LPNMHDR) lParam)->code) ||
                    (TTN_NEEDTEXTW == ((LPNMHDR) lParam)->code))
            {
                LPTOOLTIPTEXT   lpToolTipText;
                static TCHAR    szBuffer[256];

                // query tooltips for buttons on the in-place toolbar.

                lpToolTipText = (LPTOOLTIPTEXT) lParam;

                MSOCMD      msocmd;
                MSOCMDTEXT *pmsocmdtext;

                pmsocmdtext = (MSOCMDTEXT *) MemAlloc(Mt(MsoCmdText),
                    sizeof(MSOCMDTEXT) + (FORMS_BUFLEN * sizeof(TCHAR)));
                pmsocmdtext->cmdtextf = MSOCMDTEXTF_NAME;
                pmsocmdtext->cwBuf    = FORMS_BUFLEN;
                pmsocmdtext->cwActual = 0;

                msocmd.cmdID = lpToolTipText->hdr.idFrom;
                msocmd.cmdf  = 0;

                if ((UINT) msocmd.cmdID == (UINT) InPlace()->_hwndComboTag)
                {
                    msocmd.cmdID = IDM_BLOCKFMT;
                }
                else if ((UINT) msocmd.cmdID == (UINT) InPlace()->_hwndComboFont)
                {
                    msocmd.cmdID = IDM_FONTNAME;
                }
                else if ((UINT) msocmd.cmdID == (UINT) InPlace()->_hwndComboSize)
                {
                    msocmd.cmdID = IDM_FONTSIZE;
                }
                else if ((UINT) msocmd.cmdID == (UINT) InPlace()->_hwndComboColor)
                {
                    msocmd.cmdID = IDM_FORECOLOR;
                }

                QueryStatus(
                        (GUID *) &CGID_MSHTML,
                        1,
                        &msocmd,
                        pmsocmdtext);

                if (pmsocmdtext->cwActual > 0)
                {
                    _tcscpy(szBuffer, pmsocmdtext->rgwz);
                }
                else
                {
                    LoadString(
                            g_hInstResource,
                            IDS_TOOLTIP(msocmd.cmdID),
                            szBuffer,
                            ARRAY_SIZE(szBuffer));
                }

                DWORD dwVersion = GetVersion();
                if (dwVersion >= 0x80000000) // Windows 95 or Win32s with Windows 3.1
                {
                    TCHAR szTemp[256];

                    _tcscpy(szTemp, szBuffer);
                    WideCharToMultiByte(
                            CP_ACP,
                            0,
                            szTemp,
                            -1,
                            (char *) szBuffer,
                            sizeof(szBuffer),
                            NULL,
                            NULL);
                }
                lpToolTipText->lpszText = szBuffer;
                MemFree(pmsocmdtext);
                break;
            }
            // else fall through & hit the goto ReflectMessage.
        }
#endif // DBG == 1 && !WINCE

        hwndControl = ((LPNMHDR) lParam)->hwndFrom;
        goto ReflectMessage;

    case WM_VSCROLL:
    case WM_HSCROLL:
        if (!lParam)
        {
            // If lParam is NULL, should try to scroll ourselves
            //
            CMessage Message(_pInPlace->_hwnd, msg, wParam, lParam);
            hr = THR(PumpMessage(&Message, _pElemCurrent->GetFirstBranch()));
            break;
        }
        // if lParam is defined, should fall through to ReflectMessage

#ifndef WIN16
    case WM_CTLCOLORBTN:
    case WM_CTLCOLORDLG:
    case WM_CTLCOLORLISTBOX:
    case WM_CTLCOLORMSGBOX:
    case WM_CTLCOLORSCROLLBAR:
    case WM_CTLCOLORSTATIC:
    case WM_CTLCOLOREDIT:
#endif // !WIN16
    case WM_VKEYTOITEM:
    case WM_CHARTOITEM:
        hwndControl = (HWND) lParam;
        goto ReflectMessage;

#ifndef WINCE
    case WM_PARENTNOTIFY:
        if (LOWORD(wParam) == WM_CREATE ||
            LOWORD(wParam) == WM_DESTROY)
        {
            hwndControl = (HWND) lParam;

            // Hack for PhotoSuite (#94834)
            if (_pElemOleSiteActivating && LOWORD(wParam) == WM_CREATE)
            {
                COleSite * pOleSite = DYNCAST(COleSite, _pElemOleSiteActivating);

                if (!pOleSite->_hwndPrivate)
                {
                    pOleSite->_hwndPrivate = hwndControl;
                }
            }
            goto ReflectMessage;
        }
        break;
#endif // WINCE

    case WM_GETDLGCODE:
        *plResult = DesignMode() ? DLGC_WANTALLKEYS : DLGC_WANTCHARS | DLGC_WANTARROWS;
        hr = S_OK;
        break;

    case WM_ERASEBKGND:
        if (wParam != 0)
        {
            *plResult = OnEraseBkgnd((HDC)wParam);
        }
        hr = S_OK;
        break;
        
#ifdef UNIX
    //
    // Under Unix handle the standard WM_CUT & PASTE messages.
    // Again, these are generated by the TranslateUnixKeyBinding
    // call on certain keystrokes... this happens too late to 
    // conver to the right IDM_ commands so I do it here.
    //

    case WM_CUT:
    {
        MSOCMD msocmd[2];

        msocmd[0].cmdID = IDM_CUT;
        msocmd[0].cmdf = 0;
        msocmd[1].cmdID = IDM_DELETE;
        msocmd[1].cmdf = 0;

        QueryStatus((GUID *) &CGID_MSHTML,
                    2,
                    msocmd,
                    NULL);
            
        //
        // Here we either delete or cut depending on whether cut
        // is available or not.  It's usually not available because
        // it's on a password field
        //

        if ( msocmd[0].cmdf != MSOCMDSTATE_DISABLED ) {
            Exec((GUID *)&CGID_MSHTML, IDM_CUT,0, NULL, NULL);
        } 
        else 
        if ( msocmd[1].cmdf != MSOCMDSTATE_DISABLED ) {
            Exec((GUID *)&CGID_MSHTML, IDM_DELETE, 0, NULL, NULL);
        }

        hr = S_OK;
        break;
    }
    case WM_COPY:
    {
        MSOCMD msocmd;

        msocmd.cmdID = IDM_COPY;
        msocmd.cmdf = 0;

        QueryStatus((GUID *) &CGID_MSHTML,
                    1,
                    &msocmd,
                    NULL);

        if ( msocmd.cmdf != MSOCMDSTATE_DISABLED ) {
            Exec((GUID *)&CGID_MSHTML, IDM_COPY, 0, NULL, NULL);
        }

        hr = S_OK;
        break;
    }
    case WM_PASTE:
        MSOCMD msocmd;

        msocmd.cmdID = IDM_PASTE;
        msocmd.cmdf = 0;

        QueryStatus((GUID *) &CGID_MSHTML,
                    1,
                    &msocmd,
                    NULL);
            
        if ( msocmd.cmdf != MSOCMDSTATE_DISABLED ) {
            Exec((GUID *)&CGID_MSHTML, IDM_PASTE, 0, NULL, NULL);
        }

        hr = S_OK;
        break;

    //
    // These two messages implement the Unix selection copy buffer
    // (clipboard) for Trident.
    //
    // Basically, if we get "GETTEXTPRIMARY" the system is doing 
    // the equivalent of a RenderFormat wanting the selected text.
    //
    // UNDOPRIMARYSELECTION tells us someone else has selected text
    // and we should undo our selection.
    //

    case WM_GETTEXTPRIMARY:
    case WM_UNDOPRIMARYSELECTION:
        {
            //
            // Send these messages down to the current Text Selection via
            // PumpMessage
            //
            CMessage  Message(_pInPlace->_hwnd, msg, wParam, lParam);
            hr = THR(PumpMessage(&Message, _pElemCurrent->GetFirstBranch()));
            break;
        }
#endif // UNIX

    case WM_GETOBJECT :
            // ActiveX "accessibility", creation of in-context proxy
            OnAccGetObjectInContext(msg, wParam, lParam, plResult);
            hr = S_OK;  // no need to continue 
            break;

    case WM_UISTATEUPDATE:
        {
            WORD wUIStateOld = _wUIState;

            if (LOWORD(wParam) == UIS_SET)
                _wUIState |= HIWORD(wParam);
            else if (LOWORD(wParam) == UIS_CLEAR)
                _wUIState &= ~HIWORD(wParam);

            // Use XOR to see which bits changed.
            wUIStateOld ^= _wUIState;

            if (wUIStateOld & UISF_HIDEACCEL)
            {
                if (_fHaveAccelerators)
                {
                    CNotification   nf;

                    nf.ChangeAccelerator(PrimaryRoot());
                    nf.SetData((DWORD)0);
                    BroadcastNotify(&nf);
                    Assert(nf.IsDataValid());

                    // If there were any accelerators then they would
                    // have set the DWORD to non-zero
                    if (nf.DataAsDWORD() == 0)
                        _fHaveAccelerators = FALSE;
                    else
                        Invalidate();
                }
            }
            else if ((wUIStateOld & UISF_HIDEFOCUS) && _view.IsActive())
            {
                _view.InvalidateFocus();
            }
        }

        *plResult = DefWindowProc(_pInPlace->_hwnd, msg, wParam, lParam);
        hr = S_OK;
        break;

    case WM_CHECKMINIMIZED:
        // check if we went iconic        
        if ( !_fIconic )
        {
            while (hwnd)
            {
                hwndParent = hwnd;
                hwnd = GetParent(hwnd);
            }

            if ( IsIconic( hwndParent ) )
            {
                _fIconic = TRUE;
                InternetSetOption(NULL, INTERNET_OPTION_HIBERNATE_INACTIVE_WORKER_THREADS, NULL, NULL); 
                KillImgAnimTimerProc();
                if (_pCaret)
                {
                    ::DestroyCaret();
                }
            }
        }
        break;
                        
    default:

        if (msg == WM_MOUSEACTIVATE && _fPopupDoc)
        {
            *plResult = (LRESULT)MA_NOACTIVATE;
            break;
        }

#ifndef WIN16
        // different WM_MOUSEWHEEL message between Windows 95 and NT 40
        //
        if (msg != 0 && msg == g_msgMouseWheel)
        {
            POINT ptCursor;

            ::GetCursorPos(&ptCursor);
            wParam = MAKEWPARAM(HIWORD(wParam), LOWORD(wParam));
            lParam = MAKELPARAM(ptCursor.x, ptCursor.y);
            goto HandleMouseWheel;
        }
#endif // ndef WIN16
        if(msg == _g_msgHtmlGetobject)
        {
            // ActiveX "accessibility"
            OnAccGetObject(msg, wParam, lParam, plResult);
            hr = S_OK;  // Stop bubbling.
            break;
        }

#if !defined(NO_IME)
        if( (msg == _g_msgImeReconvert) && (_g_msgImeReconvert) )
        {
            goto ReconvertIME;
        }
#endif // !NO_IME

        //  All other messages get forwarded to the base class's
        //    window procedure.

        hr = THR(CServer::OnWindowMessage(msg, wParam, lParam, plResult));
        break;
    }

Cleanup:

#if DBG == 1
    if (msg == WM_PAINT || msg == WM_SETREDRAW)
    {
        TraceTag((tagPaintRedraw, "%x -%d %x %x",
                        this, msg, wParam, lParam));
    }
#endif

    // TODO should we come here with S_FALSE, or does that mean
    // defProc was not called when should?
    RRETURN1(hr, S_FALSE);

ReflectMessage:

    Assert(hwndControl);
    *plResult= SendMessage(
            hwndControl,
            msg + OCM__BASE,
            wParam,
            lParam);

    hr = S_OK;
    goto Cleanup;
}

//+------------------------------------------------------------------------
//
//  Member:     CDoc::EnableDragDrop
//
//  Synopsis:   Register or revoke drag-drop as appropriate.
//
//-------------------------------------------------------------------------
void
CDoc::EnableDragDrop(DWORD_PTR dwContext)
{
    IDropTarget *   pDT;

    if (State() >= OS_INPLACE)
    {
        if (!THR_NOTRACE(GetDropTarget(&pDT)))
        {
            BOOL    fRegHostDT = FALSE;
            if (_pHostUIHandler)
            {
                IDropTarget *   pDTOut = NULL;

                if (!_pHostUIHandler->GetDropTarget(pDT, &pDTOut))
                {
                    //cache our Drop target so it can be restored if it's overwriten by a popup
                    _pDT = pDT;
                    _pDT->AddRef();

                    // Register host's drop target
                    IGNORE_HR(RegisterDragDrop(_pInPlace->_hwnd, pDTOut));
                    fRegHostDT = TRUE;
                }
                ReleaseInterface(pDTOut);
            }

            if (!fRegHostDT)
                IGNORE_HR(RegisterDragDrop(_pInPlace->_hwnd, pDT));

            pDT->Release();
        }
    }
}


//+---------------------------------------------------------------------------
//
//  Member:     CDoc::OnMouseMessage
//
//  Synopsis:   Handle WM_MOUSEMOVE, WM_LBUTTONDOWN and so on.
//
//----------------------------------------------------------------------------

HRESULT
CDoc::OnMouseMessage(
    UINT msg,
    WPARAM wParam,
    LPARAM lParam,
    LRESULT *plResult,
    int x, int y)
{
    HRESULT         hr  = S_OK;
    CMessage        Message(_pInPlace->_hwnd, msg, wParam, lParam);
    CTreeNode *     pNodeHit = NULL;
    CTreeNode *     pNodeNewMouse = NULL;
    long            lNewSubDivision;
    BOOL            fCapture;
    DWORD           dwHitTestFlags;
    ULONG           cDie = _cDie;
    CTreeNode::CLock *pLockNodeNewMouse = 0;

    Message.pt.x = x;
    Message.pt.y = y;

    //--------------------------------------------------------------------
    //
    // NOTE(SujalP and GaryBu):
    //
    // Normally, a button down implies a BecomeCurrent() which implies
    // a transition to the UIActive state. The BecomeCurrent() happens
    // in PumpMessage(). However, we do not call BecomeCurrent() when
    // the mouse goes down on a scrollbar. This is needed to fix bugs
    // like bug48127, where clicking on the scrollbar changes the current
    // site.
    //
    // However, if we do not perform BecomeCurrent() on the button down
    // in PumpMessage(), then we have to atleast transition of UIActive
    // otherwise we will break the frame case. Consider a doc with 2 frames.
    // Lets say the focus is on the left frame. The user now clicks on
    // the right frame scrollbar. The right frame will not call BecomeCurrent
    // because the hit was on the scrollbar and hence not even transition to
    // UIActive state. Hence, we do the transition here. Note that the
    // transition has to be on a button down (not on a button up -- as it
    // was done originally) because if the object takes capture then we
    // may not get the button up message at all (scrollbar are guilty of
    // this).
    //
    // If non-client area like scrollbars are hit, then under certain hosts
    // we do not want to become UIActive either. Consider the case of Athena
    // (bug33562) where clicking on the scrollbar, takes focus away from
    // the "To:" input box of Athena. To prevent us from taking focus, we
    // ask hosts such as Athena to turn on the following doc host flag:
    // DOCHOSTUIFLAG_ACTIVATE_CLIENTHIT_ONLY. Note that we will still take
    // focus if the hit were on the body (done by PumpMessage() as explained
    // earlier).
    //
    //--------------------------------------------------------------------

    if (   State() < OS_UIACTIVE
        && !(_dwFlagsHostInfo & DOCHOSTUIFLAG_ACTIVATE_CLIENTHIT_ONLY)
        && (   msg == WM_LBUTTONDOWN
            || msg == WM_RBUTTONDOWN
            || msg == WM_MBUTTONDOWN
           )
       )
    {
        // Do not fire onfocus on the current element, because the
        // current element may change because of the button down.
        _fInhibitFocusFiring = TRUE;
        TransitionTo(OS_UIACTIVE);
        _fInhibitFocusFiring = FALSE;
    }
    // We need to check to see that we really have the capture
    // because WM_CAPTURECHANGED is not sent on all platforms.
    fCapture = FALSE;
    dwHitTestFlags = 0;

    if (HasCapture())
    {
        fCapture = TRUE;
        
        dwHitTestFlags = HT_IGNORESCROLL | HT_NOGRABTEST;
    }

    //
    // Locate the "hit" element
    //

    Message.htc = HitTestPoint(
                     &Message,
                     &pNodeHit,
                     dwHitTestFlags);

    if( !fCapture && Message.htc == HTC_NO )
    {
        hr = S_FALSE;
        goto Cleanup;
    }

    Assert( !pNodeHit || pNodeHit->IsInMarkup() );

    // IE6 bug 29944 - this is a workaround trying to 
    // fix this stress failure. This should not happen
    // however it is a safe bet to bail out at this point.
    if (pNodeHit && !pNodeHit->IsInMarkup())
    {
        hr = S_FALSE;
        goto Cleanup;
    }
    
    hr = THR( Message.SetNodeHit( pNodeHit ) );
    if( hr )
        goto Cleanup;

    if( Message.pNodeHit )
        pNodeHit->Element()->SubDivisionFromPt(Message.ptContent, &Message.lSubDivision);
    
    pNodeNewMouse = Message.pNodeHit;
    lNewSubDivision = Message.lSubDivision;

        // $$ktam: Revisit ref-counting _pLCLastMouseOver like _pNodeLastMouseOver.

    {
        // don't let the hit-tested element go away til we're ready
        if (pNodeNewMouse)
        {
            pLockNodeNewMouse = new CTreeNode::CLock;
            hr = THR( pLockNodeNewMouse->Init(pNodeNewMouse) );
            if( hr )
                goto Cleanup;
        }
        
        {
            CTreeNode * pNodeOldOver = _pNodeLastMouseOver;
            CLayoutContext *pLCOldOver = _pLCLastMouseOver;
    
            //
            // Hand the message to the site.  However, if the htc is >= HTC_GRPTOPBORDER
            // we can just hand it off to the parent.  htc >= HTC_GRPTOPBORDER implies
            // a hit on the grab handles or border.
            //
    
            if ((Message.htc >= HTC_GRPTOPBORDER) && Message.pNodeHit->Element()->IsEditable(TRUE))
            {
                pNodeNewMouse = Message.pNodeHit->GetUpdatedParentLayoutNode();
                pNodeNewMouse = (pNodeNewMouse && Message.pNodeHit->Element()->IsInMarkup())
                                    ? pNodeNewMouse
                                    : Message.pNodeHit;
            }
    
    
            // Do not send MouseOver/MouseLeave messages in response to a
            // WM_SETCURSOR! WM_SETCURSOR is not a true mouse message and Windows
            // can send us this message any time it thinks the cursor needs to be
            // redrawn. This can cause nasty situations if the script handler for
            // MouseOver/MouseOut generates another WM_SETCURSOR message! See bug
            // 13590. (MohanB)
    
            // deal with the last element under mouse
            if (    Message.message != WM_SETCURSOR
                &&  State() >= OS_INPLACE
                &&  cDie == _cDie
                &&  _pNodeLastMouseOver
                &&  pNodeNewMouse
                &&  (   _pNodeLastMouseOver != pNodeNewMouse
                     || _lSubDivisionLast != lNewSubDivision)

                // Ignore transitions from master to slave and vice versa (39135)
                && !(       _pNodeLastMouseOver->Element()->HasMasterPtr()
                        &&  _pNodeLastMouseOver->Element()->GetMasterPtr() == pNodeNewMouse->Element()
                     ||     pNodeNewMouse->Element()->HasMasterPtr()
                        &&  pNodeNewMouse->Element()->GetMasterPtr() == _pNodeLastMouseOver->Element()
                    )
               )
            {
                CTreeNode * pNodeTo = pNodeNewMouse->Ancestor(ETAG_A);
    
                CMessage MessageOut(_pInPlace->_hwnd, WM_MOUSELEAVE, NULL, lParam);
                hr = THR( MessageOut.SetNodeHit( pNodeOldOver ) );
                if( hr )
                    goto Cleanup;
                MessageOut.pt.x = x;
                MessageOut.pt.y = y;
                MessageOut.lSubDivision = _lSubDivisionLast;
                MessageOut.pLayoutContext = pLCOldOver;
    
                // set up for the 'from' & 'to' event object parameters. This part
                // is tricky. we are firing and WM_MOUSEEXIT, the this pointer is
                // the 'from' element, the 'to' element is accessed by TEMPORARILY
                // putting it into the _pNodeLastMouseOver pointer (since it is
                // redundant with the this pointer anyhow). after this call to HM
                // (which assumes the _pelemlast.. is the 'to' pointer (see CElement::
                // FireStdEventOnMessage)) the _pNodeLastMouseOver is restored for
                // the remainder of the HM calls.
                _pNodeLastMouseOver = pNodeNewMouse;
                hr = THR( _pNodeLastMouseOver->NodeAddRef() );
                if( hr )
                    goto Cleanup;
                _pLCLastMouseOver = Message.pLayoutContext;
                // if the timer is set, we want to turn it off. otherwise it is possible
                //  that the _pNodeLastMouseOver will be Nulled out from under use (root
                //  cause of bug 21062)
                if (_fMouseOverTimer)
                {
                    FormsKillTimer(this, TIMER_ID_MOUSE_EXIT);
                    _fMouseOverTimer = FALSE;
                }

                hr = THR(PumpMessage(&MessageOut, pNodeOldOver));
                //restore to fire the message itself
                if (_pNodeLastMouseOver)
                    _pNodeLastMouseOver->NodeRelease();
    
                _pNodeLastMouseOver = pNodeOldOver;
                _pLCLastMouseOver = pLCOldOver;

                // Is this node no longer connected to the primary markup? (#30760)
                if (!_pNodeLastMouseOver->IsConnectedToPrimaryMarkup())
                {
                    _pNodeLastMouseOver->NodeRelease();
                    _pNodeLastMouseOver = NULL;
                }

                if (Message.pNodeHit && !Message.pNodeHit->IsConnectedToPrimaryMarkup())
                {
                    // The hit element got nuked! Get out now before we hurt ourselves!
                    goto Cleanup;
                }
    
                // if the element being hit is not an anchor or its child then show appropriate status text
                if (!pNodeTo)
                    (void)THR_NOTRACE(SetStatusText(NULL, STL_ROLLSTATUS));
            }
    
    
            // Watch for the mouse going off the window.
    
            if (!_fMouseOverTimer)
            {
                hr = THR(FormsSetTimer(this,
                       ONTICK_METHOD(CDoc, DetectMouseExit, detectmouseexit),
                       TIMER_ID_MOUSE_EXIT,
                       300));
                if (!hr)
                    _fMouseOverTimer = TRUE;
            }
    
            //
            //  Handle tooltips
            //
    
            if (Message.pNodeHit)
            {
                ShowTooltipHelper(this, Message.pNodeHit, &Message);
            }
    
            //TODO: This used to be working in IE4 (ie401)
            //          (Pri1 BUG: 7164)
            //  but crashing in IE5. seems related to some major changes
            //  We can no longer assume GetRootDoc()->_pInPlace to be valid
            //  any more. Therefore, we need to revisit this issue to make
            //  sure that all reference to _pInPlace are safe.
            if (!_pNodeLastMouseOver)
                IGNORE_HR(SetStatusText(NULL, STL_ROLLSTATUS));
    
            hr = THR(PumpMessage(&Message, pNodeNewMouse));

            // Fire the event unless the element got nuked in event handler!
            if (pNodeNewMouse && !pNodeNewMouse->IsDead() && pNodeNewMouse->IsInViewTree())
            {
                // Now fire MouseOver if applicable
                if (    Message.message != WM_SETCURSOR
                    &&  State() >= OS_INPLACE
                    &&  cDie == _cDie
                    &&  (   _pNodeLastMouseOver != pNodeNewMouse
                         || _lSubDivisionLast != lNewSubDivision)

                    // Ignore transitions from master to slave and vice versa (39135)
                    && !(   _pNodeLastMouseOver
                         && (
                                    _pNodeLastMouseOver->Element()->HasMasterPtr()
                                &&  _pNodeLastMouseOver->Element()->GetMasterPtr() == pNodeNewMouse->Element()
                             ||     pNodeNewMouse->Element()->HasMasterPtr()
                                &&  pNodeNewMouse->Element()->GetMasterPtr() == _pNodeLastMouseOver->Element()
                            )
                        )
                   )
                {
                    Assert(_pInPlace && _pInPlace->_hwnd);
                    CMessage MessageOut(_pInPlace->_hwnd, WM_MOUSEOVER, NULL, lParam);
                    hr = THR( MessageOut.SetNodeHit( pNodeNewMouse ) );
                    if( hr )
                        goto Cleanup;
                    MessageOut.pt.x = x;
                    MessageOut.pt.y = y;
                    MessageOut.lSubDivision = lNewSubDivision;
                    MessageOut.pLayoutContext = Message.pLayoutContext;
    
                    hr = THR(PumpMessage(&MessageOut, pNodeNewMouse));

                    if (pNodeNewMouse->IsDead() || !pNodeNewMouse->IsInViewTree())
                    {
                        Verify( !CTreeNode::ReplacePtr(&_pNodeLastMouseOver, NULL) );
                        _lSubDivisionLast = 0;
                        _pLCLastMouseOver = NULL;
                    }

                    // don't assign if this is the rootsite
                    else if (pNodeNewMouse->Element() != PrimaryRoot())
                    {
                        hr = THR( CTreeNode::ReplacePtr(&_pNodeLastMouseOver, pNodeNewMouse) );
                        if( hr )
                            goto Cleanup;
                        _lSubDivisionLast = MessageOut.lSubDivision;
                        _pLCLastMouseOver = MessageOut.pLayoutContext;
                    }
                }
            }
        }
    }

    //
    // Ensure that UI Active site matches the current site on mouse button up.
    //

    if ((msg == WM_LBUTTONUP || msg == WM_RBUTTONUP || msg == WM_MBUTTONUP) &&
        (_pElemUIActive != _pElemCurrent) &&
        (cDie == _cDie ) && 
        (State() >= OS_INPLACE ))
    {
        _pElemCurrent->BecomeUIActive();
    }

    if (msg != WM_MOUSEMOVE && msg != WM_SETCURSOR)
    {
        DeferUpdateUI();
    }

Cleanup:
    if  (pLockNodeNewMouse)
    {
        delete pLockNodeNewMouse;
    }

    RRETURN1(hr, S_FALSE);
}

//+----------------------------------------------------------------------------
//
// Member: FireMouseOverTest
//
// Sysnopsis:  Timer Callback. this function's job is to detect when a mouse move
//      has taken us outside the client rectl. when that is detected, we fire
//      an exit message.
//              in any case we clear the doc flag, and we kill the timer.
//
//-----------------------------------------------------------------------------

HRESULT BUGCALL
CDoc::DetectMouseExit( UINT uTimerID)
{
    HRESULT hr = S_OK;

    if (    !_fInhibitOnMouseOver
        &&  !HasCapture()
        &&  State() >= OS_INPLACE)
    {
        POINT     ptMouse;
        HWND      hwndMouse;

        GetCursorPos(&ptMouse);

        if ((hwndMouse = WindowFromPoint(ptMouse)) != NULL
            && hwndMouse != _pInPlace->_hwnd
            && !IsDuringDrag())
        {
            if (::IsChild(_pInPlace->_hwnd, hwndMouse))
            {
                LRESULT lResult;

                // We always send mousemove messages to the document so 
                // the onmouseover/out events will get fired correctly. 
                MapWindowPoints(NULL, _pInPlace->_hwnd, &ptMouse, 1);
                IGNORE_HR(OnWindowMessage(WM_MOUSEMOVE, 0, 
                    MAKELPARAM(ptMouse.x, ptMouse.y),  &lResult));
            }
            else if (_pNodeLastMouseOver)
            {
                CMessage  Message(_pInPlace->_hwnd, WM_MOUSELEAVE, NULL, NULL);
                CTreeNode * pNode = _pNodeLastMouseOver;

                hr = THR( Message.SetNodeHit( pNode ) );
                if( hr )
                    goto Cleanup;
                Message.pt.x = -1;      // we know we are outside client rect
                Message.pt.y = -1;
                Message.pLayoutContext = _pLCLastMouseOver;

                // Don't need timer any more.

                FormsKillTimer(this, uTimerID);
                _fMouseOverTimer = FALSE;

                // Set this first in order to properly fill the EVENTPARAM from,to

                _pNodeLastMouseOver = NULL;
                _pLCLastMouseOver = NULL;
                _lSubDivisionLast = 0;
            
                IGNORE_HR(PumpMessage(&Message, pNode));

                // Release our addref on the lastmouseoverelem.

                pNode->NodeRelease();
            }
        }
    }

Cleanup:
    RRETURN( hr );
}

//+---------------------------------------------------------------------------
//
//  Member:     CDoc::OnHelp
//
//  Synopsis:   Handle keboard messages
//
//----------------------------------------------------------------------------

//#ifndef WIN16
HRESULT
CDoc::OnHelp(HELPINFO *phi)
{
    HRESULT hr      = S_FALSE;
    LRESULT lResult = TRUE;

    // If the F1 key is down or the mouse is not down, then treat it
    // like a keyboard message. Otherwise, treat it like a mouse message.

    if ((GetKeyState(VK_F1) & 0x8000) || !(GetKeyState(VK_LBUTTON) & 0x8000))
    {
        // give a chance to ShowHelp which calls IDocHostShowUI; if that failed,
        // show help ourselves
        if (S_OK != ShowHelp(NULL, 0, 0, g_Zero.pt))
        {
            CMessage Message(_pInPlace->_hwnd, WM_HELP, 0, (LPARAM)phi);

            hr = THR(PumpMessage(&Message, _pElemCurrent->GetFirstBranch()));
        }
    }
    else
    {
#ifndef WIN16
        POINT pt = phi->MousePos;
#else
        POINT pt;
        pt.x = (phi->MousePos).x;
        pt.y = (phi->MousePos).y;
#endif

        ScreenToClient(_pInPlace->_hwnd, &pt);
        hr = THR(OnMouseMessage(
                WM_HELP,
                0,
                (LPARAM)phi,
                &lResult,
                pt.x, pt.y));
    }

    RRETURN1(hr, S_FALSE);
}
//#endif // !WIN16

//+--------------------------------------------------------------------------
//
//  Member:     CDoc::OnCommand
//
//  Synopsis:   Handle WM_COMMAND
//
//----------------------------------------------------------------------------

void
CDoc::OnCommand(int idm, HWND hwndCtl, UINT code)
{
    BOOL fRestoreFocus = FALSE;

#if DBG==1
    switch (idm)
    {
    case IDM_DEBUG_TRACETAGS:
        DbgExDoTracePointsDialog(FALSE);
        return;

    case IDM_DEBUG_RESFAIL:
        DbgExShowSimFailDlg();
        return;

    case IDM_DEBUG_DUMPOTRACK:
        DbgExTraceMemoryLeaks();
        return;

    case IDM_DEBUG_BREAK:
        DebugBreak();
        return;

    case IDM_DEBUG_VIEW:
        DbgExOpenViewObjectMonitor(_pInPlace->_hwnd, (IUnknown *)(IViewObject *) this, TRUE);
        return;
    }
#endif

        //
        // These are the cases which can be handled by Exec().
        //

        VARIANTARG *pvarIn  = NULL;
        if( idm >= IDM_MIMECSET__FIRST__ && idm <= IDM_MIMECSET__LAST__ )
        {
            CODEPAGE cp = GetCodePageFromMenuID(idm);
            if (CP_UNDEFINED != cp && PrimaryMarkup()->GetCodePage() != cp)
            {
                PrimaryMarkup()->SwitchCodePage(cp);

                if (_pClientSite)
                {
                    // Let shdocvw know about it
                    VARIANT var;

                    V_VT(&var) = VT_I4;
                    V_I4(&var) = cp;

                    CTExec(_pClientSite, &CGID_ShellDocView, SHDVID_ONCODEPAGECHANGE,
                           0, &var, NULL);
                }

                IGNORE_HR(_pWindowPrimary->ExecRefresh());
            }
            return;
        }
        Exec((GUID *)&CGID_MSHTML, idm, 0, pvarIn, NULL);

        // When the user selects a combo item, pop the focus pack into the document.

        if (fRestoreFocus)
        {
            ::SetFocus (_pInPlace->_hwnd);
        }
}


//+------------------------------------------------------------------------
//
//  Member:     CDoc::OnAccGetObject
//
//  Synopsis:   Handles receipt of the WM_GET_HTML_OBJECT by the form
//
//  Input:      msg
//              wParam  Opaque, pass through.
//              lParam  idObject from NotifyWinEvent.
//
//  Returns:    LRESULT containing a IAccessible object.
//
//  Notes:      This must work in concert with the corresponding NotifyWinEvents,
//              which must pass the address of the object in idObject.
//
//-------------------------------------------------------------------------

void
CDoc::OnAccGetObject(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
{
#if !defined(_MAC) && !defined(WIN16)
    HRESULT hr;

    static DYNPROC s_dynprocLresultFromObject =
            { NULL, &g_dynlibOLEACC, "LresultFromObject" };

    if (!plResult)
        goto Cleanup;

    // Load up the LresultFromObject pointer.
    hr = THR(LoadProcedure(&s_dynprocLresultFromObject));
    if (hr)
    {
        goto Cleanup;
    }

    // Convert the punk into an lresult, which is an OS means of
    // converting the punk into a handle that can be marshalled.

    // Note: Although this returns an interface pointer, we do not
    // addref it here.  copy semantics are inplace for the lresult 
    // so this function does the requisite stuff

    //  pass out the _OmDocument, this is necessary to handle QI's (et al)
    //  properly when we are in the webbrowser.ocx and other aggregating
    //  hosts.

    *plResult = (*(LRESULT (APIENTRY *)(REFCLSID, WPARAM, IUnknown *))
            s_dynprocLresultFromObject.pfn)(IID_IHTMLDocument2, 
                                            wParam, 
                                            (IUnknown*)_pWindowPrimary->Document());

Cleanup:
#endif
    return;
}

//+--------------------------------------------------
//
//   member : OnAccGetObjectIncontenxt
//
//  Synopsis : WM_GETOBJECT processing
//          rather than passing the document pointer to oleacc
//        for them to cocreate the proxy, we can create the proxy
//        ourselves and thus save lots of work by not marshalling 
//
//
//------------------------------------------------------
// Note: lParam is intentionaly declared as a LONG since 
// Objids are 32bits everywhere (according to brendanm)
// and Win64 fills differently the upper 64bits of the
// LPARAM depending on the caller beign 32bits or 64bits
void
CDoc::OnAccGetObjectInContext (UINT msg, 
                              WPARAM wParam, 
                              LONG lParam, 
                              LRESULT *plResult)
{
#if !defined(_MAC) && !defined(WIN16)
    HRESULT         hr = S_OK;
    CAccBase *      pAccObj = NULL;
    ACCEVTRECORD    accEvtRec;

    static DYNPROC s_dynprocLresultFromObject =
            { NULL, &g_dynlibOLEACC, "LresultFromObject" };

    if (!plResult)
        goto Cleanup;

    // Load up the LresultFromObject pointer.
    hr = THR(LoadProcedure(&s_dynprocLresultFromObject));
    if (hr)
    {
        goto Cleanup;
    }

    if ( (lParam == OBJID_WINDOW) || (lParam == OBJID_CLIENT))
    {
        CWindow * pWindow = _pWindowPrimary->Window();
        
        //check if this document has a Window Accessible object associated with it.
        //if not, create one, else AddRef and return the existing one.
        hr = EnsureAccWindow(pWindow);
        if (hr)
            goto Cleanup;

        pAccObj = pWindow->_pAccWindow;
    }
    else if (lParam > 0)
    {
        hr = _aryAccEvents.GetAccEvtSource(lParam, &accEvtRec);
        if (hr)
            goto Cleanup;

        Assert(accEvtRec.pObj);

        // The base object can be a window or an element
        // We will only release the additional reference we have on the element
        // once we have created the accessible object for it, since the reference
        // we have may be the only reference left.
        if (accEvtRec.fWindow)
        {
            hr = EnsureAccWindow(DYNCAST(CWindow, accEvtRec.pObj));
            if (hr)
                goto Cleanup;

            pAccObj = DYNCAST(CWindow, accEvtRec.pObj)->_pAccWindow;
        }
        else
        {
            CElement * pElement = DYNCAST(CElement, accEvtRec.pObj);

            // if an element is out of a markup, don't try to return a pointer to it at all.
            if (pElement->HasMarkupPtr())
            {
                // Ensure the accwindow object for this element.
                if (EnsureAccWindow(pElement->GetCWindowPtr()))
                {
                    hr = E_FAIL;
                    goto Cleanup;
                }

                pAccObj = GetAccObjOfElement(pElement);
                if (!pAccObj)
                {
                    hr = E_FAIL;
                    goto Cleanup;
                }
            }
            else
            {
                hr = E_FAIL;
                goto Cleanup;
            }
        }
    }
    // else is cases such as the title bar window, system menu window etc. 
    // all have negative handles used by OLEACC.

    // Convert the punk into an lresult, which is an OS means of
    // converting the punk into a handle that can be marshalled.

    // Note: Although this returns an interface pointer, we do not
    // addref it here.  There will be an addref performed when the
    // caller call ObjectFromLresult. (This isn't in the documentation,
    // but LauraBu swears it happens.)
    //
    // Of course, this is craziness, because if the object we return
    // is deallocated before the client calls ObjectFromLResult, someone
    // is in trouble. Laura replies that inproc clients always call
    // immediately, and out-of-proc clients somehow deal with it.

    if ( pAccObj )
    {
        *plResult = (*(LRESULT (APIENTRY *)(REFCLSID, WPARAM, IUnknown *))
                s_dynprocLresultFromObject.pfn)(IID_IAccessible, 
                                                wParam, 
                                                (IAccessible *)pAccObj);
    }

Cleanup:

    // we want to bail not fail.  if ANY error happens we want to pretend nothing 
    //  was ever processed, and that the WM_GETOBJECT was never handled to begin 
    //  with. 
    if (hr || (plResult && FAILED(*plResult)))
        *plResult = S_OK;

#endif
    return;
}


//+---------------------------------------------------------------------------
//
//  Member:     CDoc::ActivateDefaultButton, protected
//
//  Synopsis:   Activate the default button on the form, if any
//
//----------------------------------------------------------------------------

HRESULT
CDoc::ActivateDefaultButton(LPMSG lpmsg)
{
    HRESULT         hr = S_FALSE;
    CElement *      pElem;   

    //  The container of this form is always given precedence,
    //    unless the currently active control is a "Push" button

    if (_pElemCurrent && _pElemCurrent->_fActsLikeButton)
    {
        pElem = _pElemCurrent;
        hr = S_OK;
    }
    else
    {
        pElem = _pElemCurrent->FindDefaultElem(TRUE);
    }

    if (pElem)
    {
        Assert(!pElem->IsEditable(/*fCheckContainerOnly*/TRUE));
        Assert(pElem->_fDefault);

        //  TODO error dialog?
        _fFirstTimeTab = FALSE;
        hr = THR(pElem->BecomeCurrentAndActive(0, NULL, NULL, TRUE));
        if (hr)
            goto Cleanup;

        hr = THR(pElem->DoClick());
    }
    else
    {
        hr = S_FALSE;
    }
Cleanup:
    RRETURN1(hr, S_FALSE);
}



//+---------------------------------------------------------------------------
//
//  Member:     CDoc::ActivateCancelButton, protected
//
//  Synopsis:   Activate the cancel button on the form, if any
//
//----------------------------------------------------------------------------

HRESULT
CDoc::ActivateCancelButton(LPMSG lpmsg)
{
    HRESULT     hr = S_FALSE;
    CElement *  pElem = _pElemCurrent;
    
    if (!pElem)
        goto Cleanup;

    if (!pElem->TestClassFlag(CElement::ELEMENTDESC_CANCEL))
    {
        // find cancel site
        pElem = _pElemCurrent->FindDefaultElem(FALSE, TRUE);
    }
    if (!pElem)
        goto Cleanup;

    Assert(!pElem->IsEditable(/*fCheckContainerOnly*/TRUE));

    _fFirstTimeTab = FALSE;
    hr = THR(pElem->BecomeCurrentAndActive(0, NULL, NULL, TRUE));
    if (!hr)
    {
        hr = THR(pElem->DoClick());
    }

Cleanup:

    RRETURN1(hr, S_FALSE);
}


//+---------------------------------------------------------------------------
//
//  Member:     CDoc::InvalidateDefaultSite
//
//  Synopsis:   Invalidate the current site
//
//----------------------------------------------------------------------------

HRESULT
CDoc::InvalidateDefaultSite()
{
    CElement * pElemDefault;
    if (!_pElemCurrent->IsEditable(/*fCheckContainerOnly*/TRUE) && !_pElemCurrent->_fDefault)
    {
        pElemDefault = _pElemCurrent->FindDefaultElem(TRUE);
        if (pElemDefault && pElemDefault != _pElemCurrent)
        {
            pElemDefault->Invalidate();
        }
    }

    RRETURN(S_OK);
}

//+------------------------------------------------------------------------
//
//  Member:     CDoc::FixZOrder
//
//  Synopsis:   Inserts the given site's window at the proper place in
//              physical Z order, given the site's position in the logical
//              Z order as well as its current OLE state
//
//-------------------------------------------------------------------------
void
CDoc::FixZOrder()
{
    _view.SetFlag(CView::VF_DIRTYZORDER);
}


//+------------------------------------------------------------------------
//
//  Member:     CDoc::SetZOrder
//
//  Synopsis:   Moves the current selection in the logical Z order.
//
//  Arguments:  [zorder]    action
//
//  Returns:    HRESULT
//
//-------------------------------------------------------------------------
HRESULT
CDoc::SetZOrder(int zorder)
{
    CLayout *   pLayoutCurrent = _pElemCurrent->GetUpdatedLayout();


    if (!pLayoutCurrent)
        goto Cleanup;


    FixZOrder();
    Invalidate();

    //  TODO send OnDataChange?

Cleanup:
    return S_OK;
}


//+------------------------------------------------------------------------
//
//  Member:     CDoc::ShowErrorDialog
//
//-------------------------------------------------------------------------
HRESULT
CWindow::ShowErrorDialog(VARIANT_BOOL *pfRet)
{
    HRESULT hr = S_OK;
    VARIANT varIn;
    CVariant varOut;
    CDoc * pDoc = Doc();
    CDoEnableModeless dem(pDoc, this);

    if (pDoc->_dwLoadf & DLCTL_SILENT)
        goto Cleanup;    

    V_VT(&varIn) = VT_UNKNOWN;
    V_UNKNOWN(&varIn) = (IUnknown*)(IPrivateUnknown *) Document();

    if (pDoc->_pHostUICommandHandler && !pDoc->_fOutlook98)
    {
        hr = pDoc->_pHostUICommandHandler->Exec(
                &CGID_DocHostCommandHandler,
                OLECMDID_SHOWSCRIPTERROR,
                0,
                &varIn,
                &varOut);
        if (!hr)
            goto Cleanup;
    }

    pDoc->EnsureBackupUIHandler();
    if (pDoc->_pBackupHostUIHandler)
    {
        IOleCommandTarget * pBackupHostUICommandHandler;

        hr = pDoc->_pBackupHostUIHandler->QueryInterface(IID_IOleCommandTarget,
                (void **) &pBackupHostUICommandHandler);
        if (hr)
            goto Cleanup;

        hr = pBackupHostUICommandHandler->Exec(
                &CGID_DocHostCommandHandler,
                OLECMDID_SHOWSCRIPTERROR,
                0,
                &varIn,
                &varOut);

        ReleaseInterface(pBackupHostUICommandHandler);
    }

Cleanup:
    if (V_VT(&varOut) == VT_BOOL)
        *pfRet = V_BOOL(&varOut);   
    RRETURN(hr);
}


//+------------------------------------------------------------------------
//
//  Member:     CDoc::ShowContextMenu
//
//  Synopsis:   Returns the context menu based on the sub-menu id.
//
//-------------------------------------------------------------------------

HRESULT
CDoc::ShowContextMenu(
        int x,
        int y,
        int id,
        CElement * pMenuObject)
{
    HRESULT                 hr = S_FALSE;   // Assume we keep bubbling.
#ifndef NO_MENU
    IOleCommandTarget *     pCommandTarget = NULL;
    IDispatch *             pDispatch = NULL;
    CElement::CLock         lock(pMenuObject);
    GDIPOINT                pt;
    BOOL                    fSkipHostUIHandler;

    _fDisableReaderMode = TRUE;
    _pMenuObject = pMenuObject;
   
    pt.x = x;
    pt.y = y;

    // DEBUG menu is only valid in debug builds. 
    if (GetAsyncKeyState(VK_CONTROL) < 0)
    {    
#if DBG==1 && !defined(WINCE)     
        id = CONTEXT_MENU_DEBUG;
#else
        if (id == CONTEXT_MENU_DEBUG)
            goto Cleanup;
#endif
    }


    // Query the host UIHandler to handle the context menu
    hr = THR(QueryInterface(
            IID_IOleCommandTarget,
            (void **) &pCommandTarget));
    if (hr)
        goto Cleanup;

    if (pMenuObject)
    {
        pMenuObject->QueryInterface(
                IID_IDispatch,
                (void **) &pDispatch);
    }
    
    // if pMenuObject==NULL or doesn't support IDispatch
    // then default to the document's IDispatch
    if (!pDispatch)
    {
        hr = THR(QueryInterface(
                IID_IDispatch,
                (void **) &pDispatch));
        if (hr)
            goto Cleanup;
    }

    
    // for active desktop items, trident is directly hosted by shell32, which also handles context menus
    // if we are in an active desktop item, we want trident context menus not shell context menus, 
    // so we need to bypass the shell32 UI handler
    fSkipHostUIHandler = DocIsDeskTopItem(this) && pMenuObject && !pMenuObject->IsInPrimaryMarkup();


    if (    !fSkipHostUIHandler
#if DBG==1
        &&  id != CONTEXT_MENU_DEBUG           // Don't let hosts override debug menu                 
#endif        
       )
    {
        if (_pHostUIHandler)
        {
            _fInhibitOnMouseOver = TRUE;
            hr = _pHostUIHandler->ShowContextMenu(
                id,
                (POINT *)&pt,           //cast is for win16, doesn't affect 32bit.
                pCommandTarget,
                pDispatch);
            
            _fInhibitOnMouseOver = FALSE;

            // Host has displayed context menu, forms3 will not display it.
            if (!hr)
                goto Cleanup;
        }
    }

    // If we don't allow a selection, don't allow a contex menu either
    // More detail: We want to disallow context menus on everything that
    // matched the following conditions:
    //      1. we're in a dialog.
    //      2. we're not an text input control (textbox, textarea, etc).
    // DisallowSelection checks if we're in a dialog and if the layout
    // has the _fAllowSelectionInDialog bit set. That bit is currently only
    // set on textareas, htmlareas, and text inputs.
    if (pMenuObject->DisallowSelection())
    {
        hr = S_OK;
        goto Cleanup;
    }

    
    // Either we don't have a _pHostUIHandler, or it didn't return S_OK.  Go to our backup.
    EnsureBackupUIHandler();     
    if (_pBackupHostUIHandler)    
    {
        _fInhibitOnMouseOver = TRUE;
        hr = THR(_pBackupHostUIHandler->ShowContextMenu(        
            id,            
            (POINT *)&pt,           //cast is for win16, doesn't affect 32bit.           
            pCommandTarget,            
            pDispatch));

        _fInhibitOnMouseOver = FALSE;
        
        // Backup has displayed context menu        
        hr = S_OK;
        goto Cleanup;
    }     
    

Cleanup:
    _pMenuObject = NULL;
    ReleaseInterface(pCommandTarget);
    ReleaseInterface(pDispatch);

    _fDisableReaderMode = FALSE;

#endif // NO_MENU
    RRETURN1(hr, S_FALSE);
}


//+------------------------------------------------------------------------
//
//  Member:     CDoc::InsertMenuExt
//
//  Arguments:  hMenu       The menu to insert in
//
//  Synopsis:   Takes the hMenu passed in and then adds a separator
//              followed by all of the menu extensions.  These are
//              insterted if and where IDM_MENUEXT_PLACEHOLDER is.
//
//  Returns:    HRESULT
//
//-------------------------------------------------------------------------

DeclareTag(tagMenuExt, "MenuExt", "Menu Extensions");

HRESULT
CDoc::InsertMenuExt(HMENU hMenu, int id)
{
    HRESULT     hr = S_OK;
    int         nExts;

    Assert(IsMenu(hMenu));

    // If our placeholder is gone or was never there, then just exit
    if(GetMenuState(hMenu, IDM_MENUEXT_PLACEHOLDER, MF_BYCOMMAND) == (UINT) -1)
    {
        goto Cleanup;
    }

    //
    // Add the context menu extensions
    //
    nExts = _pOptionSettings->aryContextMenuExts.Size();
    if(nExts != 0)
    {
        CONTEXTMENUEXT **   ppCME;
        int                 nExtCur = 0;
        int                 nExtFirst = 0;

        // Limit the number of extensions
        if(nExts > IDM_MENUEXT_LAST__ - IDM_MENUEXT_FIRST__)
        {
            nExts = IDM_MENUEXT_LAST__ - IDM_MENUEXT_FIRST__;
        }

        TraceTag((tagMenuExt, "%d menu exts", nExts));

        //
        // Add each of the extensions
        //

        for(ppCME = _pOptionSettings->aryContextMenuExts;
            nExtCur < nExts;
            nExtCur++, ppCME++)
        {
            if((*ppCME)->dwContexts & (0x1 << id))
            {
                Assert(IDM_MENUEXT_FIRST__ + nExtCur <= IDM_MENUEXT_LAST__);

                TraceTag((tagMenuExt, "Inserting %d: %ls",
                          IDM_MENUEXT_FIRST__ + nExtCur,
                          (LPTSTR)(*ppCME)->cstrMenuValue));

                if(!InsertMenu(hMenu,                           // The Context Menu
                               IDM_MENUEXT_PLACEHOLDER,         // The item to insert before
                               MF_BYCOMMAND|MF_STRING,          // by item ident and str value
                               IDM_MENUEXT_FIRST__ + nExtCur,   // the command id
                               (LPTSTR)(*ppCME)->cstrMenuValue))// The menu name
                {
                    hr = GetLastWin32Error();
                    goto Cleanup;
                }

                if(!nExtFirst)
                {
                    nExtFirst = IDM_MENUEXT_FIRST__ + nExtCur;
                }
            }
        }

        //
        // Add a separator if necessary
        //

        if(nExtFirst)
        {
            if(!InsertMenu(hMenu,                       // The context menu
                           nExtFirst,                   // The item to insert before
                           MF_BYCOMMAND|MF_SEPARATOR,   // by item ident
                           0,                           // the command id
                           NULL))                       // type value
            {
                hr = GetLastWin32Error();
                goto Cleanup;
            }
        }
    }

    //
    // Remove our placeholder
    //

    if(!DeleteMenu(hMenu,                   // The context menu
                   IDM_MENUEXT_PLACEHOLDER, // The item to delete
                   MF_BYCOMMAND))           // by item ident
    {
        hr = GetLastWin32Error();
        goto Cleanup;
    }

Cleanup:
    RRETURN(hr);
}


//+------------------------------------------------------------------------
//
//  Member:     CDoc::ShowDragContextMenu
//
//  Synopsis:   Shows the drag context menu upon a right button drop.
//
//  Arguments:  ptl           Where to pop the menu up
//              dwAllowed     Allowed actions for drag
//              piSelection   The choice selected by the menu.
//
//  Returns:    If the user selected a choice on the menu, S_OK.
//              If the menu was brought down, S_FALSE, errors otherwise.
//
//-------------------------------------------------------------------------
HRESULT
CDoc::ShowDragContextMenu(POINTL ptl, DWORD dwAllowed, int *piSelection, LPTSTR lptszFileType)
{
#ifdef NO_DRAGDROP
    return S_OK;
#else
    HRESULT                     hr;

    HMENU   hMenu;
    HMENU   hCtxMenu;
    HCURSOR hOldCursor;
    HCURSOR hArrow = LoadCursor(NULL, IDC_ARROW);

    if(hArrow == NULL)
        RRETURN(GetLastError());

    hMenu = TW32(0, LoadMenu(GetResourceHInst(), MAKEINTRESOURCE(IDR_DRAG_CONTEXT_MENU)));
    if (!hMenu)
        RRETURN(GetLastWin32Error());

    hCtxMenu = GetSubMenu(hMenu, 0);

    // Modify the sub menu based on whether particular choices are
    // available.
    if (!(dwAllowed & DROPEFFECT_MOVE) || (_fFromCtrlPalette))
    {
        EnableMenuItem(hCtxMenu,
            DROPEFFECT_MOVE,
            MF_BYCOMMAND | MF_GRAYED);
    }
    if (!(dwAllowed & DROPEFFECT_COPY))
    {
        EnableMenuItem(hCtxMenu,
            DROPEFFECT_COPY,
            MF_BYCOMMAND | MF_GRAYED);
    }

    //
    // Get the old cursor, and make new cursor into arrow
    //
    hOldCursor = ::SetCursor(hArrow);

    hr = THR(FormsTrackPopupMenu(
            hCtxMenu,
            TPM_LEFTALIGN | TPM_RIGHTBUTTON,
            ptl.x + CX_CONTEXTMENUOFFSET,
            ptl.y + CY_CONTEXTMENUOFFSET,
            NULL,
            piSelection));

    // Now set back the old cursor
    ::SetCursor(hOldCursor);

    RRETURN1(hr, S_FALSE);
#endif // NO_DRAGDROP
}

#if DBG == 1
#ifndef UNIX

DeclareTag(tagZOrder, "ZOrder", "Form ZOrder information");

#define GetWinText(hwnd, sz, cch) if (!GetWindowText(hwnd, sz, cch)) _tcscpy(sz, _T("(null)"));

void
DisplayChildZOrder(HWND hwnd)
{
    TCHAR szClass[255], szTitle[255];
    LONG  lStyle;

    if (!hwnd)
        return;

    GetClassName(hwnd, szClass, 255);
    GetWinText(hwnd, szTitle, 255);

    TraceTag((tagZOrder, "Parent Window: %p, %ls, %ls", hwnd, szClass, szTitle));

    HWND hwndChild = GetWindow(hwnd, GW_CHILD);

    if (!hwndChild)
    {
        TraceTag((tagZOrder, "No Child Windows!"));
        return;
    }

    GetClassName(hwndChild, szClass, 255);
    GetWinText(hwndChild, szTitle, 255);
    lStyle = GetWindowLong(hwndChild, GWL_EXSTYLE);

    TraceTag((tagZOrder, "Highest Child Window: %p, %ls, %ls%ls",
                   hwndChild, szClass, szTitle,
                   (lStyle & WS_EX_TOPMOST) ? _T(" (Topmost)") : _T("")
                   ));

    while ((hwndChild = GetWindow(hwndChild, GW_HWNDNEXT)) != NULL)
    {
        GetClassName(hwndChild, szClass, 255);
        GetWinText(hwndChild, szTitle, 255);
        lStyle = GetWindowLong(hwndChild, GWL_EXSTYLE);

        TraceTag((tagZOrder, "   Next Child Window: %p, %ls, %ls%ls",
                       hwndChild, szClass, szTitle,
                       (lStyle & WS_EX_TOPMOST) ? _T(" (Topmost)") : _T("")
                       ));
    }

    return;
}
#else
void
DisplayChildZOrder(HWND hwnd)
{}
#endif // !UNIX
#endif // DBG

//+---------------------------------------------------------------------------
//
//  Member:     CDoc::HasCapture
//
//  Synopsis:   
//
//----------------------------------------------------------------------------

BOOL CDoc::HasCapture(CElement *pElement)
{
    if (!_pInPlace)
        return FALSE;

    if (!pElement)
    {
        if (_aryStackCapture.Size())
        {
            Assert(GetCapture());
            return TRUE;
        }
    }
    else
    {
        int i, lSizeStack = _aryStackCapture.Size();

        for ( i = 0; i < lSizeStack; i ++ )
        {
            if (_aryStackCapture[i]->_pElement == pElement)
            {
                Assert(GetCapture());
                return TRUE;
            }
        }
    }
    return GetCapture();
}


//+---------------------------------------------------------------------------
//
//  Member:     CDoc::SetMouseCapture
//
//  Synopsis:   Mouse capture utilities.
//
//----------------------------------------------------------------------------

void CDoc::SetMouseCapture(
    PFN_ELEMENT_MOUSECAPTURE   pfnTo,
    CElement                *pElement,
    BOOL                    fContainer,
    BOOL                    fFireOnNodeHit)
{
    int i;
    int lSizeStack = _aryStackCapture.Size();
    CElementCapture *pCapture;

    TraceTag((tagCapture, "SetCapture %x %x %ls-%d  fCont: %d  _fOnLose: %d",
            this, pElement, (pElement ? pElement->TagName() : L"null"),
            (pElement ? pElement->SN() : 0), fContainer, _fOnLoseCapture));

    if (_fOnLoseCapture)
    {
        //
        // make sure we empty the whole capture array
        //
        pElement = NULL;
    }

    if (!pElement)
    {
        //
        // request for a full capture release
        //

        if (lSizeStack)
        {
            for (i = lSizeStack - 1; i >= 0; i --)
            {
                if (    !_aryStackCapture[i]->_fFiredEvent
                    &&  !_aryStackCapture[i]->_pElement->_fExittreePending)
                {
                    _aryStackCapture[i]->_fFiredEvent = TRUE;
                    _fOnLoseCapture = TRUE;
                    
                    _aryStackCapture[i]->_pElement->Fire_onlosecapture();
                   
                    _fOnLoseCapture = FALSE;
                }
                //
                // during the event firing, the stack might be insane
                //
                lSizeStack = _aryStackCapture.Size();

                //
                // if insane
                // go out of the loop
                //

                if (!lSizeStack)
                    break;
                
                delete _aryStackCapture[i];
            }
            _aryStackCapture.DeleteAll();
        }

        if (    State() < OS_INPLACE
            &&  ::GetCapture() == _hwndCached)
        {
            // NOTE(sujalp): When we are no longer inplace, but we
            // may still have our window around in CDoc::hwndCached.
            // Check if this window has the capture and if it does
            // then release the capture. If not then, probably some
            // other window has captured the mouse so do not release it.
#if DBG==1
            Assert(!TLS(fHandleCaptureChanged));
#endif
            ::ReleaseCapture();
        }
        else
        {
            SetCapture(FALSE);
        }
        return;
    }

    //
    // verify if pElement has the capture, if so, do nothing
    //

    for (i = lSizeStack-1; i >= 0 && _aryStackCapture[i]->_pElement != pElement; i--);

    if (i >= 0)
        return;

    //
    // Add the new capture object
    //

    CElementCapture *pCaptureNew = new CElementCapture(pfnTo, pElement, fContainer, fFireOnNodeHit);

    if (!pCaptureNew)
        return;

    pCapture = GetLastCapture();

    //
    // Container capture means that the element takes
    // the capture and all mouse messages/events should
    // be targeting to it.
    // When the capture is not a container capture, events
    // will be targeting to the elements scoped inside it
    //

    if (!pCapture || !HasContainerCapture(pElement->GetFirstBranch()))
    {
        //
        // the Doc doesn't have container capture
        // push the capture in the stack
        //
        IGNORE_HR(_aryStackCapture.Append(pCaptureNew));
        if (!pCapture)
        {
            SetCapture(TRUE);
        }
        return;
    }
    //
    //
    // If we've got a capture object which is different from the argument
    // object, tell it that we're losing capture
    // 
    CMessage Message((MSG *)NULL);

    Message.message = WM_CAPTURECHANGED;

    // Tell the captured object that it's losing the capture
    IGNORE_HR( PumpMessage(&Message, NULL) );

    //
    // delete the previous capture if it is not Container Capture
    // has to do the container part
    //

    if (!pCapture->_fFiredEvent && !pCapture->_pElement->_fExittreePending)
    {
        pCapture->_fFiredEvent = TRUE;
        _fOnLoseCapture = TRUE;

        pCapture->_pElement->Fire_onlosecapture( );

        _fOnLoseCapture = FALSE;
    }

    //
    // during the event firing, the stack might be insane
    // if insane
    // don't do any capture work
    //

    if (!_aryStackCapture.Size())
    {
        delete pCaptureNew;
        return;
    }

    delete pCapture;
    _aryStackCapture.Delete(lSizeStack - 1);

    IGNORE_HR(_aryStackCapture.Append(pCaptureNew));
}

//+--------------------------------------------------------------------------
//
//  Member:     CDoc::ClearMouseCapture
//
//  Synopsis:   Releases the capture object without notification. Used by
//              objects to revoke their own capture.
//              if pvObject is NULL, we will release all captures
//
//---------------------------------------------------------------------------

void
CDoc::ClearMouseCapture(CElement *pElement)
{
    int lSizeStack2, lSizeStack = _aryStackCapture.Size();
    int i, j;

    if (!lSizeStack)
        return;

    if (!pElement || _aryStackCapture[0]->_pElement == pElement)
    {
        SetMouseCapture(NULL, NULL);
        return;
    }

    //
    // Search for the capture element
    //
    
    for (i = lSizeStack-1; i > 0 && _aryStackCapture[i]->_pElement != pElement; i--);

    if (i == 0)
        return;

    Assert(lSizeStack > 1);
    j = i;

    for (i = lSizeStack - 1; i >= j; i --)
    {
        if (    !_aryStackCapture[i]->_fFiredEvent
            &&  !_aryStackCapture[i]->_pElement->_fExittreePending)
        {
            _aryStackCapture[i]->_fFiredEvent = TRUE;
            _fOnLoseCapture = TRUE;

            _aryStackCapture[i]->_pElement->Fire_onlosecapture( );
            
            _fOnLoseCapture = FALSE;
        }
        //
        // during the event firing, the stack might be insane
        //
        lSizeStack2 = _aryStackCapture.Size();

        //
        // if insane
        // go out of the loop
        //

        if (!lSizeStack2)
            return;
        delete _aryStackCapture[i];
    }
    _aryStackCapture.DeleteMultiple(j, lSizeStack - 1);

    //
    // Since there is still at least one capture object in the stack
    // we don't release window capture
    //
}

//+--------------------------------------------------------------------------
//
//  Member:     CDoc::ReleaseDetachedCaptures
//
//  Synopsis:   Releases the capture object which is no longer in the tree
//
//---------------------------------------------------------------------------

void
CDoc::ReleaseDetachedCaptures()
{
    int lSizeStack = _aryStackCapture.Size();
    int i, j;

    if (!lSizeStack)
        return;

    if (!_aryStackCapture[0]->_pElement->GetFirstBranch())
    {
        SetMouseCapture(0, 0);
        return;
    }

    for ( i = 1; i < lSizeStack && _aryStackCapture[i]->_pElement->GetFirstBranch(); i ++ );

    if (i == lSizeStack)
        return;

    j = i;

    for (; i < lSizeStack; i ++ )
    {
        delete _aryStackCapture[i];
    }
    _aryStackCapture.DeleteMultiple(j, lSizeStack - 1);
}

CElementCapture * CDoc::GetLastCapture()
{
    int iSize = _aryStackCapture.Size();

    if (!iSize)
        return NULL;

    return _aryStackCapture[iSize - 1];
}
 
CElementCapture::CElementCapture(PFN_ELEMENT_MOUSECAPTURE pfn, CElement *pElem, BOOL fContainer, BOOL fFireOnNodeHit)
{
    //
    // Args must be either both NULL == Release capture
    // Or both non-null == Set capture
    //
    // AssertSz(pfn && pElem || !pfn && !pElem, "Bad Arguments");

    _pfn        = pfn;
    _pElement   = pElem;

    Assert (_pElement);
    _pElement->AddRef();
    _fContainer = fContainer;
    _fFiredEvent = FALSE;
    _fFireOnNodeHit = fFireOnNodeHit;
}

CElementCapture::~CElementCapture()
{
    if (_pElement)
    {
        _pElement->Release();
    }
}

//+---------------------------------------------------------------------------
//
//  Member:     CDoc::HasContainerCapture(CTreeNode *pNode)
//
//  Synopsis:   .
//
//----------------------------------------------------------------------------

BOOL    CDoc::HasContainerCapture(CTreeNode *pNode)
{
    CElementCapture *pCapture = GetLastCapture();
    BOOL            fHasContainerCapture = FALSE;

    if( pCapture )
    {
        //
        // if we have container capture, or the node passed in is the capture element,
        // then we have capture
        //
        fHasContainerCapture = pCapture->_fContainer || 
                                ( pNode && (pNode == pCapture->_pElement->GetFirstBranch() ) );

        if( !fHasContainerCapture )
        {
            //
            // We know that container capture is NOT set, so all children of the current capture
            // element should continue to get mouse events as normal.  So 
            // we need to check to see if the node passed in is a child of the element which
            // currently has capture.  If it is a child, then we DO NOT have container capture, 
            // route the message as normal.  If the node in question IS NOT a child of the
            // element which has setup capture, we need to hand all mouse messages to the 
            // element with capture, and we need to return TRUE.
            //
            CTreeNode *pMaster = pNode->GetNodeInMarkup( pCapture->_pElement->GetMarkupPtr() );

            fHasContainerCapture = !pMaster || !pMaster->SearchBranchToRootForScope( pCapture->_pElement );
        }
    }
    
    return fHasContainerCapture;
}

//+---------------------------------------------------------------------------
//
//  Member:     CDoc::PumpMessage
//
//  Synopsis:   Fires the event and then hands over the message to pTarget. If
//              ptarget is NULL, the message is handed over to the capture
//              object.
//
//----------------------------------------------------------------------------
HRESULT
CDoc::PumpMessage(CMessage * pMessage, CTreeNode * pNodeTarget, BOOL fPerformTA)
{
    HRESULT     hr              = S_OK;
    BOOL        fInRecursion    = _fInPumpMessage;
    BOOL        fSaveModalState = _fModalDialogInScript; // to handle recursion
    BOOL        fEventCancelled = FALSE;
    CTreeNode * pNodeHit        = pMessage->pNodeHit;
    CTreeNode * pCurNode        = NULL;
    CTreeNode * pNodeFireEvent  = NULL;
    CTreeNode * pNodeFireEventSrcElement = NULL;
    CElement  * pElemCurrentOld = _pElemCurrent;
    ULONG       cDie            = _cDie;
    BOOL        fMouseMessage   =   (   (pMessage->message >= WM_MOUSEFIRST)
                                     && (pMessage->message <= WM_MOUSELAST))
                                  ||    (pMessage->message == WM_SETCURSOR)
                                  ||    (pMessage->message == WM_CAPTURECHANGED)
                                  ||    (pMessage->message == WM_MOUSELEAVE)
                                  ||    (pMessage->message == WM_MOUSEOVER);
    BOOL        fSiteSelected   = FALSE;
    EVENTINFO evtInfo;
    BOOL fHadCapture = FALSE;
    CTreeNode * pNodePrimaryRoot   = PrimaryRoot()->GetFirstBranch();
    IHTMLEditor* ped = _pIHTMLEditor;
    BOOL        fPopupKeyMsg    =   _fPopupDoc 
                                &&  pMessage->message >= WM_KEYFIRST
                                &&  pMessage->message <= WM_KEYLAST;
     
    if ( ped )
    {
        ped->AddRef();
    }
    
    Assert(pNodePrimaryRoot);
    
    //  TODO(laszlog): This should be changed into a real fix!
    if ( ! _pElemCurrent || _pElemCurrent->Tag() == ETAG_DEFAULT )
    {
        return S_OK;
    }

    Assert(pMessage);

    AssertSz((  pNodeTarget
                && !pNodeTarget->IsDead()
                && pNodeTarget->Element()
                && pNodeTarget->IsInMarkup())
            || HasCapture() // TODO: investigate this (marka)
            || !GetLastCapture()
            || GetLastCapture()->_pElement->IsInMarkup()
            || pMessage->message == WM_MOUSEOVER
            || pMessage->message == WM_MOUSELEAVE,
             "Trying to send a windows message to an element not in the tree");

    _fInPumpMessage = TRUE;

    // In browse mode, if the mouse is clicked on a child of BUTTON,
    // we want to send that click to the BUTTON instead. IE5 #34796, #26572
    switch (pMessage->message)
    {
    case WM_LBUTTONDOWN:
    case WM_MBUTTONDOWN:
    case WM_RBUTTONDOWN:
    case WM_LBUTTONUP:
    case WM_MBUTTONUP:
    case WM_RBUTTONUP:
        if (pNodeHit && !pNodeHit->Element()->IsEditable(TRUE))
        {
            CTreeNode * pNodeLoop = pNodeHit;

            while (pNodeLoop && pNodeLoop->Tag() != ETAG_BUTTON)
            {
                pNodeLoop = pNodeLoop->Parent();
            }
            if (pNodeLoop)
            {
                if (pNodeLoop != pNodeHit)
                {
                    if (pNodeTarget == pNodeHit)
                    {
                        pNodeTarget = pNodeLoop;
                    }

                    // pNodeHit is parented by a BUTTON.
                    pNodeHit = pNodeLoop;
                    hr = THR( pMessage->SetNodeHit(pNodeHit) );
                    if( hr )
                        goto Cleanup;
                    pMessage->lSubDivision = 0;
                }
            }
        }
    }

    switch (pMessage->message)
    {
    case WM_LBUTTONDOWN:
    case WM_MBUTTONDOWN:
    case WM_RBUTTONDOWN:
        Assert(pNodeHit || !pNodeTarget);
        hr = THR( CTreeNode::ReplacePtr(&_pNodeGotButtonDown, pNodeHit) );
        if( hr )
            goto Cleanup;
        _fFirstTimeTab = FALSE;
        // fall-through
    case WM_KEYDOWN:
    case WM_SYSKEYDOWN:
        if (fPerformTA)
        {
            if (g_dwPlatformID == VER_PLATFORM_WIN32_NT && g_dwPlatformVersion >= 0x00050000)
            {
                extern BOOL IsNumpadKey(CMessage *);
                if (!(pMessage->message == WM_SYSKEYDOWN && IsNumpadKey(pMessage)))
                {
                    switch (pMessage->wParam)
                    {
                    case VK_MENU:
                        if (_wUIState & UISF_HIDEACCEL)
                            SendMessage(_pInPlace->_hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEACCEL | UISF_HIDEFOCUS), 0);
                        break;
                    case VK_RIGHT:
                    case VK_LEFT:
                    case VK_UP:
                    case VK_DOWN:
                    case VK_TAB:
                        if (_wUIState & UISF_HIDEFOCUS)
                            SendMessage(_pInPlace->_hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);
                        break;                
                    }
                }
            }
        }

        // Kill any task that's waiting to scroll or jump to a bookmark
        // TODO (jbeda) we have to figure out he right markup to send these to

        // Set a flag to indicate that the user has started interacting.
        // We use this flag to cancel restoring history like scroll/caret
        // positions.    
        {
            CMarkup * pMarkupCurrent = _pElemCurrent->GetMarkup();

            pMarkupCurrent->_fUserInteracted = TRUE;
            pMarkupCurrent->TerminateLookForBookmarkTask();
        }
        break;

    case WM_CONTEXTMENU:
        // if generated by the mouse, this WM_CONTEXTMENU should be treated as
        // a mouse message.  This will cause proper mouse capture behavior.
            if ((GET_X_LPARAM(pMessage->lParam) != -1) || 
                (GET_Y_LPARAM(pMessage->lParam) != -1))
            {
                fMouseMessage = TRUE;
            }
        break;
    }

#ifndef WIN16
    // NB (cthrash) If we get a TranslateAccelerator message directly from
    // our host, we need to be sensitive to whether the host's message loop
    // gets Unicode our Multibyte window messages.  For our internal purposes,
    // we need the WM_CHAR message to be in Unicode, so we need to convert
    // when appropriate.
    if (pMessage->message == WM_CHAR &&
        ( VER_PLATFORM_WIN32_WINDOWS == g_dwPlatformID ||
          !IsWindowUnicode( pMessage->hwnd ) ))
    {
        UINT uKeyboardCodePage = GetKeyboardCodePage();
        
        if (!_bLeadByte && IsDBCSLeadByteEx(uKeyboardCodePage, (BYTE)pMessage->wParam))
        {
            _bLeadByte = BYTE(pMessage->wParam);
            goto Cleanup;
        }
        else
        {
            // Translate the character to unicode
            CHAR ach[2];
            wchar_t wch;

            if (_bLeadByte)
            {
                ach[0] = _bLeadByte;
                ach[1] = (BYTE)pMessage->wParam;
            }
            else
            {
                ach[0] = (BYTE)pMessage->wParam;
                ach[1] = 0;
            }

            WHEN_DBG(int cchDbg =)
            MultiByteToWideChar(uKeyboardCodePage, 0,
                                ach, ach[1] ? 2 : 1, &wch, 1);

            //
            // HACK (cthrash) For codepages which don't support the Euro symbol,
            // convert multibyte value of 128 simply results in U+0080.  Since
            // this is never what the user wanted (it's a control character), we
            // hack this to convert to the Euro.
            //  

            if (   wch == 128
                && ach[1] == 0
                && BYTE(ach[0]) == 128)
            {
                wch = WCH_EURO;
            }
                
            AssertSz(1== cchDbg, "Char convert failed");
            pMessage->wParam = wch;
            _bLeadByte = 0;
        }
    }
#endif // WIN16

    // Determine who should fire the event for this message

    if (!pNodeHit)
    {
        pNodeFireEvent = _pElemCurrent ? _pElemCurrent->GetFirstBranch() : NULL;
    }
    else
    {
        if (pMessage->pElementEventTarget)
        {
            pNodeFireEvent = pMessage->pElementEventTarget->GetFirstBranch();
            pNodeFireEventSrcElement = pNodeHit;
        }
        else
        {
            pNodeFireEvent = pNodeHit;
        }
    }

    if (!pNodeFireEvent)
        goto Cleanup;

    Assert(pNodeFireEvent);

    _fModalDialogInScript = FALSE;


    //
    // release all detached captures
    //

    ReleaseDetachedCaptures();

    if (    fMouseMessage
        &&  HasContainerCapture(pNodeFireEvent))
    {
        CElementCapture *pCapture = GetLastCapture();
        pNodeFireEventSrcElement = pNodeFireEvent;
        if (!pCapture->_fFireOnNodeHit)
            pNodeFireEvent = pCapture->_pElement->GetFirstBranch();

        Assert(pNodeFireEventSrcElement);
        Assert(pNodeFireEvent);
        if (pNodeFireEventSrcElement->GetMarkup() != pNodeFireEvent->GetMarkup())
        {
            // Fix for #86892
            // If a master has capture and its slave is the src element, then change
            // the src element to be the master, to ensure encapsulation.
            if (    pNodeFireEventSrcElement->Element()->HasMasterPtr()
                &&  pNodeFireEventSrcElement->Element()->GetMasterPtr() == pNodeFireEvent->Element())
            {
                pNodeFireEventSrcElement = NULL;
            }
            else
            {
                pNodeFireEventSrcElement = NULL;
            }
        }
        fHadCapture = TRUE;
    }

    if (!fPerformTA)
    {
        // We have already fired the event for certain messages in the TA pass
        switch (pMessage->message)
        {
        case WM_KEYDOWN:
        case WM_KEYUP:
        case WM_SYSKEYDOWN:
        case WM_SYSKEYUP:
            pMessage->fEventsFired = TRUE;
            break;
        }
    }

    if (    !pMessage->fEventsFired
         && pNodeFireEvent != pNodePrimaryRoot)
    {
    
        CTreeNode::CLock lock;
        hr = THR( lock.Init( pNodeFireEvent ) );
        if( hr )
            goto Cleanup;


        fEventCancelled = (S_FALSE != THR(pNodeFireEvent->Element()->
                                            FireStdEventOnMessage(pNodeFireEvent, pMessage, NULL, pNodeFireEventSrcElement, & evtInfo )));

        Assert( ! evtInfo._fDontFireEvent ||
                ( evtInfo._fDontFireEvent && !fEventCancelled ));
                
        if ( evtInfo._pParam == NULL &&
             ( pMessage->message == WM_IME_STARTCOMPOSITION ||
               pMessage->message == WM_IME_ENDCOMPOSITION || 
               pMessage->message == WM_IME_COMPOSITIONFULL ||
               pMessage->message == WM_IME_CHAR ||
               pMessage->message == WM_IME_COMPOSITION ||
               pMessage->message == WM_IME_NOTIFY ||
               pMessage->message == WM_INPUTLANGCHANGE ||
               pMessage->message == WM_IME_REQUEST ))
        {
            hr = THR(CreateIMEEventInfo(pMessage, &evtInfo, pNodeFireEvent->Element()));
            if (FAILED(hr))
                goto Cleanup;
        }

        //
        // This is a slight hack.  Well more than a slight hack.  IN IE 5.0, the editor
        // listened directly to windows messages.  Windows fires the WM_LBUTTONDBLCLK message
        // in the following order.  LBUTTONDOWN, LBUTTONUP, LBUTTONDBLCLK, LBUTTONUP.  In 
        // IE 5.0, the editor would intercept the LBUTTONDBLCLK message and fire the
        // OnSelectStart event, notifying our clients that a selection was beginning.
        //
        // In IE 5.5, the editor is only using trident events in order to do all its work.
        // Trident, however, sends the DBLCLICK message in the following order.
        //  LBUTTONDOWN, LBUTTONUP, CLICK, LBUTTONUP, LBUTTONDBLCLK.
        //
        // Therefore the editor is unable to send the OnSelectStart event at the same
        // time as it used to, breaking other applications because of compat.  See bug
        // 86923 for more information.
        // 
        if( evtInfo._pParam == NULL && pMessage->message == WM_LBUTTONDBLCLK )
        {
            hr = THR( CreateDblClickInfo( pMessage, &evtInfo, pNodeFireEvent, pNodeFireEventSrcElement ) );
            if( FAILED(hr) )
                goto Cleanup;
        }
                                                
        if (!AreWeSaneAfterEventFiring(pMessage, cDie))
            goto Cleanup;

        // script may have removed pNodeFireEvent from the tree
        if(pNodeFireEvent->IsDead())
            pNodeFireEvent = pNodeFireEvent->Element()->GetFirstBranch();

        if(!pNodeFireEvent || !pNodeFireEvent->Element()->IsInMarkup())
            goto Cleanup;
    }

    //
    // TODO-NEW_EDIT_EVENTS : Why not always do the currency change irregardless of who has capture ?
    //                        This code is being reworked to put SetEditContext inside of mshtmled. Shouldn't be an issue
    // 

    // Do special stuff for MouseDown/Up messages

    //
    // TODO - designmode check is just for setting currency issue. 
    // Remove this after my set edit context work goes in.
    // 
    if (!HasContainerCapture(pNodeFireEvent) || pNodeTarget && pNodeTarget->GetMarkup()->_fDesignMode )
    {
        switch (pMessage->message)
        {
            case WM_LBUTTONDOWN:
            case WM_MBUTTONDOWN:
            case WM_RBUTTONDOWN:

            if ( !HasContainerCapture(pNodeFireEvent) )
            {
                // Give currency to the site under the mouse, unless the event
                // handler changed currency or the mouse is on a selection.
                // Leave OLEsites alone; let them set their own currency in HandleMessage (#60183)
                Assert(pNodeHit);

                if (_fFirstTimeTab)
                {
                    CLayout * pLayoutClient = pNodeHit->GetMarkup()->GetElementClient()->GetUpdatedLayout();
                    RECT      rc;

                    pLayoutClient->GetRect(&rc);
                    if (PtInRect(&rc, pMessage->pt))
                    {
                        _fFirstTimeTab = FALSE;
                    }
                }
            }

            pCurNode = pMessage->pNodeHit;
            
            if (    pCurNode
                && ! fSiteSelected
                &&  _pElemCurrent == pElemCurrentOld
                &&  (   DifferentScope(_pElemCurrent, pCurNode )
                    ||  _lSubCurrent != pMessage->lSubDivision || !_pInPlace->_fFocus)
                &&  !pCurNode->Element()->TestClassFlag(CElement::ELEMENTDESC_OLESITE))
            {
                Assert( pCurNode->GetMarkup() );
                
                //
                // Look for the topmost element client - for frameset pages. 
                //
                CElement* pElemFrame = NULL;
                CWindow * pWindowTop = pCurNode->GetMarkup()->Window() ? pCurNode->GetMarkup()->Window()->Window() : NULL;                
                CMarkup* pMarkupFrame = pWindowTop && pWindowTop->_pWindowParent ? pWindowTop->_pWindowParent->_pMarkup : NULL;                

                pElemFrame = pMarkupFrame ? pMarkupFrame->GetElementClient() : NULL ;            
                
                if (    pMessage->htc <  HTC_GRPTOPBORDER
                    &&  (   pCurNode->Element()->Tag() != ETAG_BODY
                        ||  (   pMessage->htc != HTC_HSCROLLBAR
                            &&  pMessage->htc != HTC_VSCROLLBAR)) 

                        //
                        // Currently our editing in framesets in desing mode doesn't work
                        // properly. A click UI-activates, not site-selects
                        // the code below makes this behavior consistent
                        //
                        
                        ||  ( pElemFrame && 
                              pElemFrame->Tag() == ETAG_FRAMESET &&
                              pElemFrame->IsEditable( FALSE ) &&
                              pCurNode->GetMarkup()->IsScrollingElementClient( pCurNode->Element()) ))                            
                {
                    BOOL        fYieldFailed    = FALSE;
                    CElement *  pElemCurrentNew = pCurNode->Element();

#ifdef UNIX
                    if ((pMessage->message == WM_MBUTTONDOWN) &&  
                        pMessage->pNodeHit->Element()->IsEditable())
                    {
                        // Get selected text before it's gone.
                        HANDLE hszText = MwGetPrimarySelectionData();
                        if(hszText)
                        {
                            g_uxQuickCopyBuffer.GetTextSelection(hszText, FALSE, NULL);
                            GlobalFree(hszText);
                        }
                    }
#endif
                    LONG lButton = 0;

                    switch( pMessage->message )
                    {
                        case WM_LBUTTONDOWN:
                        lButton = 1;
                        break;

                        case WM_RBUTTONDOWN:
                        lButton = 2;
                        break;

                        case WM_MBUTTONDOWN:
                        lButton = 4;
                        break;
                    }

                        
                    if (S_FALSE == NotifySelectionHelper(  EDITOR_NOTIFY_BEFORE_CURRENCY_CHANGE, pElemCurrentNew)
                        || S_OK != pElemCurrentNew->BubbleBecomeCurrent(
                            pMessage->lSubDivision, &fYieldFailed, pMessage, TRUE, lButton ))
                    {
                        if (fYieldFailed)
                        {
                            fEventCancelled = TRUE;
                        }
                    }

                    if (!AreWeSaneAfterEventFiring(pMessage, cDie))
                        goto Cleanup;

                    // script may have removed pNodeFireEvent from the tree
                    if(pNodeFireEvent->IsDead())
                        pNodeFireEvent = pNodeFireEvent->Element()->GetFirstBranch();

                    if(!pNodeFireEvent || !pNodeFireEvent->Element()->IsInMarkup())
                        goto Cleanup;
                }
            }

            // if a modal dialog was brought by the event handler, treat it
            // as a cancel
            fEventCancelled = (fEventCancelled || _fModalDialogInScript);
            
            break;

            case WM_LBUTTONUP:
            case WM_MBUTTONUP:
            case WM_RBUTTONUP:
            // If a MouseUp is cancelled, release capture. Any further cleanup
            // required should be performed by the site/element by handling
            // WM_CAPTURECHANGED
            if (fEventCancelled)
            {
                SetMouseCapture (NULL, NULL);
            }
            break;
        }
    }

    //
    // release all detached captures
    //

    ReleaseDetachedCaptures();

    if (!fEventCancelled)
    {
        hr = S_FALSE;
    }

    if (!fEventCancelled && !fPopupKeyMsg)
    {
        if (fPerformTA)
        {
            // don't bubbel through element handlemessage
            hr = THR(PerformTA(pMessage, & evtInfo ));
        }
        else
        {
            if (    fMouseMessage
                &&  HasContainerCapture(pNodeFireEvent))
            {
                hr = THR(GetLastCapture()->CallCaptureFunction(pMessage));                
            }
            else
            {
                // if we lost capture in the event handler or while setting focus,
                // send the message to the element that fired the event
                pNodeTarget = pNodeFireEvent;
                Assert(pNodeTarget->IsInMarkup());
                //
                // Since the htmlInputFile version of CInput has an input
                // and a button that we're simulating, we have to 
                // do this hack.
                //
                BOOL fGiveMsgToEditor = TRUE;

                // Verify that we have a CInput and the message is a WM_CHAR
                if ((pMessage->message  == WM_CHAR) && 
                    (_pElemCurrent->Tag() == ETAG_INPUT))
                {
                    CInput * pInput = DYNCAST(CInput, _pElemCurrent);

                    // We only want the htmlInputFile version of CInput
                    if (pInput->GetType() == htmlInputFile)
                    {
                        // Give the message only if the button doesn't have focus.
                        fGiveMsgToEditor = !pInput->_fButtonHasFocus;
                    }
                }

                // give first chance to the Editor in certain cases

                //
                // TODO-NEW_EDIT_EVENTS : Completely remove all the above fGiveMsgToEditor junk
                //
                                
                if (  fGiveMsgToEditor )
                {
                    if ( ! fHadCapture )
                    {
                        if ( evtInfo._pParam != NULL  ||
                             pMessage->message == WM_KEYDOWN )
                        {

                            if ( evtInfo._pParam == NULL )
                            {
                                evtInfo._pParam = new EVENTPARAM(this, pNodeFireEvent->Element(), NULL, TRUE /*fInitState*/, FALSE /*fPush*/ );
                                
                                const PROPERTYDESC_BASIC* pDesc;
                                
                                InitEventParamForKeyEvent(
                                    evtInfo._pParam , 
                                    pNodeFireEvent, 
                                    pMessage, 
                                    (int*)&pMessage->wParam, 
                                    & pDesc );

                                evtInfo._dispId = (DISPID)(pDesc->c);                               
                            }
                            Assert( evtInfo._pParam != NULL );
                        
                            hr = THR(HandleSelectionMessage(pMessage, FALSE, & evtInfo, HM_Pre ));
                        }                            
                    }
                }

                if (   hr == S_FALSE && 
                       pMessage->pNodeHit &&
                       pMessage->message == WM_LBUTTONUP &&
                       ! pMessage->pNodeClk )
                {
                    SetClick( pMessage );
                }             

                if (hr == S_FALSE)
                {
                    while (pNodeTarget && pNodeTarget->IsInMarkup() && pNodeTarget != pNodePrimaryRoot)
                    {
                        Assert( pNodeTarget && pNodeTarget->IsInMarkup() );
                        // TODO (jbeda) I don't feel confident enough in this
                        // code to let the assert above handle this problem.  Break
                        // out here if we aren't in the markup because otherwise
                        // we *will* crash
                        if (!pNodeTarget || !pNodeTarget->IsInMarkup())
                            break;

                        hr = THR(pNodeTarget->Element()->HandleMessage(pMessage));

                        if (hr != S_FALSE)
                            break;


                        if (pNodeTarget->Element()->HasMasterPtr())
                        {
                            pNodeTarget = pNodeTarget->Element()->GetMasterPtr()->GetFirstBranch();
                        }
                        else
                        {
                            pNodeTarget = pNodeTarget->Parent();
                        }
                        
                        // Don't bubble up WM_MOUSEHWEEL into frameset. Frameset sent the message
                        // down
                        if (    pMessage->message == WM_MOUSEWHEEL
                            &&  pNodeTarget
                            &&  pNodeTarget->Tag() == ETAG_FRAMESET)
                        {
                            break;
                        }
                    }
                }
                // give a second and final chance to the Editor
                if ((hr == S_FALSE) && fGiveMsgToEditor)
                {
                    if ( ! fHadCapture 
                        //
                        // We only call HandleSelectionMessage if there was an OM message
                        // like the windows message.
                        //
                        && ( evtInfo._pParam != NULL  ||
                             ( pMessage->message == WM_KEYDOWN && !fPerformTA ))
                    )
                    {
                        //
                        // TODO-NEW_EDIT_EVENTS : Talk to mohan abt. a better way of acheiving this.
                        //

                        if (evtInfo._pParam == NULL)
                        {
                            evtInfo._pParam = new EVENTPARAM(this, pNodeFireEvent->Element(), NULL, TRUE /*fInitState*/, FALSE /*fPush*/ );
                            
                            const PROPERTYDESC_BASIC* pDesc;
                            
                            InitEventParamForKeyEvent(
                                evtInfo._pParam , 
                                pNodeFireEvent, 
                                pMessage, 
                                (int*)&pMessage->wParam, 
                                & pDesc );

                            evtInfo._dispId = (DISPID)(pDesc->c);                               
                        }
                        Assert( evtInfo._pParam != NULL );
                        
                        hr = THR(HandleSelectionMessage(pMessage, FALSE, & evtInfo , HM_Post ));

                    }                        

                }
            }

            if (hr == S_FALSE)
            {
                LRESULT lr;

                hr = THR(OnDefWindowMessage(pMessage->message, pMessage->wParam, pMessage->lParam, &lr));
            }

        }

        if (!SUCCEEDED(hr))
            goto Cleanup;

        if (S_OK == hr && !fPerformTA)
        {
            CElementCapture * pCapture = GetLastCapture();
            CElement *pelFireTarget = NULL;
            CTreeNode *pNode = NULL;
            BOOL    fFireDblClick = FALSE;

            // Do click or dblclick as appropriate
            if (_fGotDblClk && pMessage->message == WM_LBUTTONUP)
            {
                fFireDblClick = TRUE;
                if (HasContainerCapture(pNodeHit))
                {
                    pelFireTarget = pCapture->_pElement;
                }
                else if (pNodeHit)
                {
                    pelFireTarget = pNodeHit->Element();
                    pNode = pNodeHit;
                }
            }
            else if (pMessage->pNodeClk)
            {
                if (HasContainerCapture(pMessage->pNodeClk) )
                {
                    pelFireTarget = pCapture->_pElement;
                }
                else
                {
                    pelFireTarget = pMessage->pNodeClk->Element();
                    pNode = pMessage->pNodeClk;
                }                
            }
            else if (pMessage->message == WM_LBUTTONUP && HasCapture())
            {
                if (!HasContainerCapture(pNodeHit))
                    pelFireTarget = pNodeHit->Element();
                else
                    pelFireTarget = pCapture->_pElement;
            }

            BOOL fDoOleSite;
            EVENTINFO clkEvtInfo;
            
            if (!pelFireTarget || pelFireTarget->TestClassFlag(CElement::ELEMENTDESC_OLESITE))
            {
                fDoOleSite = TRUE;
                clkEvtInfo._fDontFireEvent = TRUE;                
            }
            else
            {
                fDoOleSite = FALSE;
            }                
                
            if (!fDoOleSite || fFireDblClick)
            {
                if (pelFireTarget)
                {
                    if (fFireDblClick)
                    {
                        hr = THR(pelFireTarget->Fire_ondblclick(pNode, pMessage ? pMessage->lSubDivision : 0, & clkEvtInfo ));
                        _fGotDblClk = FALSE;                    
                    }
                    else
                    {
                        hr = THR(pelFireTarget->DoClick(pMessage, pNode, FALSE, & clkEvtInfo ));
                    }
                }

                pMessage->fSelectionHMCalled = FALSE;
                if ( clkEvtInfo._pParam )
                {
                    hr = THR(HandleSelectionMessage(pMessage, FALSE, & clkEvtInfo, HM_Post ));                    
                }
            }
                
        }
    }

    if (!AreWeSaneAfterEventFiring(pMessage, cDie))
        goto Cleanup;

    if (_fInvalInScript && !fInRecursion)
    {
        Assert(_pInPlace);
        Assert(_pInPlace->_hwnd);
        UpdateWindow(_pInPlace->_hwnd);
    }
Cleanup:
    switch (pMessage->message)
    {
    case WM_LBUTTONUP:
    case WM_MBUTTONUP:
    case WM_RBUTTONUP:
    case WM_KILLFOCUS:
        Verify( !CTreeNode::ReplacePtr(&_pNodeGotButtonDown, NULL) );
        break;
    }

    if ( ped )
    {
        ped->Release();
    }

    _fModalDialogInScript = fSaveModalState;
    _fInPumpMessage = fInRecursion;


    RRETURN1(hr, S_FALSE);
}


//+---------------------------------------------------------------------------
//
//  Member:     CDoc::AreWeSaneAfterEventFiring
//
//  Synopsis:   Determines if anything during the event firing has changed
//              things adversly.
//
//----------------------------------------------------------------------------
BOOL
CDoc::AreWeSaneAfterEventFiring(CMessage *pMessage, ULONG cDie)
{
    BOOL fAreWeSane = FALSE;

    if (State() < OS_INPLACE || cDie != _cDie)
        goto Cleanup;

    if ((   pMessage->message >= WM_MOUSEFIRST
         && pMessage->message <= WM_MOUSELAST
        )
        || pMessage->message == WM_SETCURSOR
       )
    {
        if (   pMessage->pNodeHit
            && !pMessage->pNodeHit->Element()->IsInMarkup()
           )
            goto Cleanup;
    }

    fAreWeSane = TRUE;

Cleanup:
    return fAreWeSane;
}
